On Tue, Jun 19, 2012 at 05:24:26PM +0800, Wanlong Gao wrote: > add new virt-diff tool > > Signed-off-by: Wanlong Gao <gaowanl...@cn.fujitsu.com> > --- > > Hi Rich, > This just a thought of virt-diff tool, I send it out ASAP > to ask your opinions. > > Now, I just implement two guest, one SEED guest and one > DIFF guest, and the patch is wholly untested. > Please take patient to review. > And it's hard for me to start two guest handler, if you > have time, please help or just handle this work. > > PS, I attended the LinuxCon Japan this month, so the plan > delayed.
The outline of the code seems to be: * create two handles * add both disks * inspect both disks * mount-local both disks on separate temporary directories * run regular 'diff' command across the temporary directories These is a reasonable approach for code simplicity, but I guess it'll be very slow in practice. > cat/virt-diff.c | 423 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 423 insertions(+) > create mode 100644 cat/virt-diff.c > > diff --git a/cat/virt-diff.c b/cat/virt-diff.c > new file mode 100644 > index 0000000..4bd8730 > --- /dev/null > +++ b/cat/virt-diff.c > @@ -0,0 +1,423 @@ > +/* virt-diff > + * Copyright (C) 2012 FUJITSU LIMITED. > + * Copyright (C) 2010-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 <inttypes.h> > +#include <unistd.h> > +#include <getopt.h> > +#include <fcntl.h> > +#include <locale.h> > +#include <assert.h> > +#include <time.h> > +#include <libintl.h> > + > +#include "human.h" > +#include "progname.h" > + > +#include "guestfs.h" > +#include "options.h" > + > +#ifndef O_CLOEXEC > +#define O_CLOEXEC 0 > +#endif > + > +/* libguestfs handle for seed. */ > +static guestfs_h *sg; > + > +/* libguestfs handle for temp. */ > +static guestfs_h *g; > + > +static int live = 0; > +static int verbose = 0; > +static int keys_from_stdin = 0; > +static int echo_keys = 0; > +static const char *libvirt_uri = NULL; > +static int inspector = 1; > + > +static doing_diff = 1; > + > +static inline char * > +diff_bad_case (char const *s) > +{ > + return (char *) s; > +} > + > +static void __attribute__((noreturn)) > +diff_usage (int status) > +{ > + if (status != EXIT_SUCCESS) { > + fprintf (stderr, _("Try `%s --help' for more information.\n"), > + program_name); > + } else { > + fprintf (stdout, > + _("%s: Show the differences between seed Guest and the others\n" > + "Copyright (C) 2012 Fujitsu Limited.\n" > + "Copyright (C) 2010-2012 Red Hat Inc.\n" > + "Usage:\n" > + "\n" > + "\n" > + "Options:\n" > + " -a|--add image Add image\n" > + " --checksum[=...] Display file checksums\n"), > + program_name); > + } > + exit (status); > +} > + > + > +/* Simple implementation of decryption: look for any crypto_LUKS > + * partitions and decrypt them, then rescan for VGs. This only works > + * for Fedora whole-disk encryption. WIP to make this work for other > + * encryption schemes. > + */ > +static void > +diff_inspect_do_decrypt (guestfs_h *g) > +{ > + char **partitions = guestfs_list_partitions (g); > + if (partitions == NULL) > + exit (EXIT_FAILURE); > + > + int need_rescan = 0; > + size_t i; > + for (i = 0; partitions[i] != NULL; ++i) { > + char *type = guestfs_vfs_type (g, partitions[i]); > + if (type && STREQ (type, "crypto_LUKS")) { > + char mapname[32]; > + make_mapname (partitions[i], mapname, sizeof mapname); > + > + char *key = read_key (partitions[i]); > + /* XXX Should we call guestfs_luks_open_ro if readonly flag > + * is set? This might break 'mount_ro'. > + */ > + if (guestfs_luks_open (g, partitions[i], key, mapname) == -1) > + exit (EXIT_FAILURE); > + > + free (key); > + > + need_rescan = 1; > + } > + free (type); > + } > + > + free_strings (partitions); > + > + if (need_rescan) { > + if (guestfs_vgscan (g) == -1) > + exit (EXIT_FAILURE); > + if (guestfs_vg_activate_all (g, 1) == -1) > + exit (EXIT_FAILURE); > + } > +} > + > +static void > +diff_inspect_mount_root (guestfs_h *g, const char *root) > +{ > + char **mountpoints = guestfs_inspect_get_mountpoints (g, root); > + if (mountpoints == NULL) > + exit (EXIT_FAILURE); > + > + /* Sort by key length, shortest key first, so that we end up > + * mounting the filesystems in the correct order. > + */ > + qsort (mountpoints, count_strings (mountpoints) / 2, 2 * sizeof (char *), > + compare_keys_len); > + > + size_t i; > + size_t mount_errors = 0; > + for (i = 0; mountpoints[i] != NULL; i += 2) { > + int r; > + r = guestfs_mount_ro (g, mountpoints[i+1], mountpoints[i]); > + if (r == -1) { > + /* If the "/" filesystem could not be mounted, give up, else > + * just count the errors and print a warning. > + */ > + if (STREQ (mountpoints[i], "/")) > + exit (EXIT_FAILURE); > + mount_errors++; > + } > + } > + > + free_strings (mountpoints); > + > + if (mount_errors) > + fprintf (stderr, _("%s: some filesystems could not be mounted > (ignored)\n"), > + program_name); > +} > + > +/* This function implements the -i option. */ > +static void > +diff_inspect_mount (guestfs_h *g) > +{ > + diff_inspect_do_decrypt (g); > + > + char **roots = guestfs_inspect_os (g); > + if (roots == NULL) > + exit (EXIT_FAILURE); > + > + if (roots[0] == NULL) { > + fprintf (stderr, > + _("%s: no operating system was found on this disk\n" > + "\n" > + "If using guestfish '-i' option, remove this option and instead\n" > + "use the commands 'run' followed by 'list-filesystems'.\n" > + "You can then mount filesystems you want by hand using the\n" > + "'mount' or 'mount-ro' command.\n" > + "\n" > + "If using guestmount '-i', remove this option and choose the\n" > + "filesystem(s) you want to see by manually adding '-m' option(s).\n" > + "Use 'virt-filesystems' to see what filesystems are available.\n" > + "\n" > + "If using other virt tools, this disk image won't work\n" > + "with these tools. Use the guestfish equivalent commands\n" > + "(see the virt tool manual page).\n"), > + program_name); > + free_strings (roots); > + exit (EXIT_FAILURE); > + } > + > + if (roots[1] != NULL) { > + fprintf (stderr, > + _("%s: multi-boot operating systems are not supported\n" > + "\n" > + "If using guestfish '-i' option, remove this option and instead\n" > + "use the commands 'run' followed by 'list-filesystems'.\n" > + "You can then mount filesystems you want by hand using the\n" > + "'mount' or 'mount-ro' command.\n" > + "\n" > + "If using guestmount '-i', remove this option and choose the\n" > + "filesystem(s) you want to see by manually adding '-m' option(s).\n" > + "Use 'virt-filesystems' to see what filesystems are available.\n" > + "\n" > + "If using other virt tools, multi-boot operating systems won't > work\n" > + "with these tools. Use the guestfish equivalent commands\n" > + "(see the virt tool manual page).\n"), > + program_name); > + free_strings (roots); > + exit (EXIT_FAILURE); > + } > + > + root = roots[0]; > + free (roots); > + > + diff_inspect_mount_root (root); > +} > + > +int > +main (int argc, int *argv[]) > +{ > + /* set global program name that is not polluted with libtool artifacts. */ > + set_program_name (argv[0]); > + bindtextdomain (PACKAGE, LOCALEBASEDIR); > + textdomain (PACKAGE); > + > + enum { HELP_OPTION = CHAR_MAX + 1 }; > + > + static const char *options = "s:d:"; > + static const struct option long_options[] = { > + {"seed", 1, 0, 's'}, > + {"domain", 1, 0, 'd'}, > + {0, 0, 0, 0} > + }; Eventually you'll need to support all the other options too, or at least --help. > + struct drv *sdrv = NULL; > + struct drv *drv = NULL; > + int c; > + > + sg = guestfs_create (); > + if (sg == NULL) { > + fprintf (stderr, _("guestfs_create: failed to create seed handle\n")); > + exit (EXIT_FAILURE); > + } > + > + g = guestfs_create (); > + if (g == NULL) { > + fprintf (stderr, _("guestfs_create: failed to create comparison > handle\n")); > + exit (EXIT_FAILURE); > + } > + > + argv[0] = bad_case (program_name); > + > + for (;;) { > + c = getopt_long (argc, argv, options, long_options, &option_index); > + if (c == -1) break; > + > + switch (c) { > + case 0: > + if (STREQ (long_options[option_index].name, "seed")) { > + if (sdrv) { > + fprintf(stderr, _("....")); > + exit (EXIT_FAILURE); > + } > + sdrv = calloc (1, sizeof (struct drv)); > + if (!sdrv) { > + perror ("malloc"); > + exit (EXIT_FAILURE); > + } > + sdrv->type = drv_d; > + sdrv->nr_drives = -1; > + sdrv->d.guest = optarg; > + sdrv->next = NULL; > + } else if (STREQ (long_options[option_index].name, "domain")) { > + drv = calloc ("malloc"); > + if (!drv) { > + perror ("malloc"); > + exit (EXIT_FAILURE); > + } > + drv->type = drv_d; > + drv->nr_drives = -1; > + drv->d.guest = optarg; > + drv->next = NULL; > + } else { > + fprintf (stderr, _("%s: unknown long option: %s (%d)\n"), > + program_name, long_options[option_index].name, > option_index); > + exit (EXIT_FAILURE); > + } > + break; > + > + case 'd': > + OPTION_d; > + break; > + > + case 's': > + if (sdrv) { > + fprintf(stderr, _("....")); > + exit (EXIT_FAILURE); > + } > + sdrv = calloc (1, sizeof (struct drv)); > + if (!sdrv) { > + perror ("malloc"); > + exit (EXIT_FAILURE); > + } > + sdrv->type = drv_d; > + sdrv->nr_drives = -1; > + sdrv->d.guest = optarg; > + sdrv->next = NULL; > + break; > + > + case HELP_OPTION: > + usage (EXIT_SUCCESS); > + > + default: > + usage (EXIT_FAILURE); > + } > + } > + > + if (sdrv == NULL) > + usage (EXIT_FAILURE); > + if (drv == NULL) > + usage (EXIT_FAILURE); > + > + struct guestfs_add_domain_argv optargs = { > + .bitmask = 0; > + .libvirturi = NULL; > + .readonly = 1; > + .allowuuid = 1; > + .readonlydisk = "read"; > + }; > + nr = guestfs_add_domain_argv (sg, sdrv->d.guest, &optargs); > + if (r == -1) > + exit (EXIT_FAILURE); > + sdrv->nr_drives = nr; > + nr = guestfs_add_domain_argv (g, drv->d.guest, &optargs); > + if (r == -1) > + exit (EXIT_FAILURE); > + drv->nr_drives = nr; > + > + if (guestfs_launch (sg) == -1) > + exit (EXIT_FAILURE); > + if (guestfs_launch (g) == -1) > + exit (EXIT_FAILURE); > + > + diff_inspect_mount (sg); > + diff_inspect_mount (g); > + > + char stempdir = "/tmp/sGuestXXXXXX"; > + char tempdir = "/tmp/GuestXXXXXX"; > + char sdir[256]; > + char dir[256]; > + > + if (mkdtemp (stempdir) == NULL) { > + perror ("mkdtemp"); > + exit (EXIT_FAILURE); > + } > + if (mkdtemp (tempdir) == NULL) { > + perror ("mkdtemp"); > + exit (EXIT_FAILURE); > + } > + > + if (guestfs_mount_local (sg, stempdir, -1) == -1) > + exit (EXIT_FAILURE); > + if (guestfs_mount_local (g, tempdir, -1) == -1) > + exit (EXIT_FAILURE); > + > + spid = fork (); > + if (spid == -1) { > + perror ("fork"); > + exit (EXIT_FAILURE); > + } > + if (spid == 0) { > + while (doing_diff) > + sleep (1); > + guestfs_umount_local (sg, GUESTFS_UMOUNT_LOCAL_RETRY, 1, -1); > + _exit (EXIT_SUCCESS); > + } > + > + pid = fork(); > + if (pid == -1) { > + perror ("fork"); > + exit (EXIT_FAILURE); > + } > + if (pid == 0) { > + while (doing_diff) > + sleep (1); > + guestfs_umount_local (g, GUESTFS_UMOUNT_LOCAL_RETRY, 1, -1); > + _exit (EXIT_SUCCESS); > + } The doing_diff variable isn't shared between forked processes, so the child processes will simply busy-wait forever. In any case the main program never even sets doing_diff. This code is going to be far more complex in the final version. > + if (guestfs_mount_local_run (sg) == -1) > + exit (EXIT_FAILURE); > + if (guestfs_mount_local_run (g) == -1) > + exit (EXIT_FAILURE); > + > + const char *dir = argv[optind]; > + char system_arg[BUFSIZ]; > + sprintf (system_arg, "diff -urN %s%s %s%s", stempdir, dir, > + tempdir, dir); > + system(system_arg); > + > + waitpid (spid, NULL, 0); > + waitpid (pid, NULL, 0); > + > + if (guestfs_umount (sg, "/") == -1) > + exit (EXIT_FAILURE); > + if (guestfs_umount (g, "/") == -1) > + exit (EXIT_FAILURE); > + > + free_drives (sdrvs); > + free_drives (drv); > + > + guestfs_close (sg); > + guestfs_close (g); > + > + exit (EXIT_SUCCESS); > +} > -- > 1.7.11.rc0 Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://et.redhat.com/~rjones/virt-top _______________________________________________ Libguestfs mailing list Libguestfs@redhat.com https://www.redhat.com/mailman/listinfo/libguestfs