Wanlong Gao
2012-Aug-06 08:11 UTC
[Libguestfs] [PATCH V2] virt-diff: add new virt-diff tool
add new virt-diff tool Signed-off-by: Wanlong Gao <gaowanlong at cn.fujitsu.com> --- Hi Rich, It can work now, please give some comments. ;) Cheers, Wanlong Gao cat/Makefile.am | 20 ++- cat/virt-diff.c | 525 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ po/POTFILES | 1 + 3 files changed, 545 insertions(+), 1 deletion(-) create mode 100644 cat/virt-diff.c diff --git a/cat/Makefile.am b/cat/Makefile.am index f7c763a..5f6a986 100644 --- a/cat/Makefile.am +++ b/cat/Makefile.am @@ -27,7 +27,7 @@ EXTRA_DIST = \ CLEANFILES = stamp-virt-cat.pod stamp-virt-ls.pod stamp-virt-filesystems.pod -bin_PROGRAMS = virt-cat virt-filesystems virt-ls +bin_PROGRAMS = virt-cat virt-filesystems virt-ls virt-diff SHARED_SOURCE_FILES = \ ../fish/config.c \ @@ -91,6 +91,24 @@ virt_ls_LDADD = \ $(top_builddir)/src/libguestfs.la \ ../gnulib/lib/libgnu.la +virt_diff_SOURCES = \ + ../fish/keys.c \ + virt-diff.c + +virt_diff_CFLAGS = \ + -DGUESTFS_WARN_DEPRECATED=1 \ + -I$(top_srcdir)/src -I$(top_builddir)/src \ + -I$(top_srcdir)/fish \ + -I$(srcdir)/../gnulib/lib -I../gnulib/lib \ + -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ + $(LIBCONFIG_CFLAGS) + +virt_diff_LDADD = \ + $(LIBCONFIG_LIBS) \ + $(top_builddir)/src/libguestfs.la \ + ../gnulib/lib/libgnu.la + # Manual pages and HTML files for the website. man_MANS = virt-cat.1 virt-filesystems.1 virt-ls.1 diff --git a/cat/virt-diff.c b/cat/virt-diff.c new file mode 100644 index 0000000..c2c59a6 --- /dev/null +++ b/cat/virt-diff.c @@ -0,0 +1,525 @@ +/* virt-diff + * Copyright (C) 2012 Fujitsu Limited. + * 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 <termios.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 <sys/wait.h> + +#include "c-ctype.h" + +#include "human.h" +#include "progname.h" + +#include "guestfs.h" +#include "options.h" + +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + +int keys_from_stdin = 0; +int echo_keys = 0; +/* libguestfs handle for seed. */ +static guestfs_h *sg; + +/* libguestfs handle for temp. */ +static guestfs_h *dg; + +const char *libvirt_uri = NULL; + +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) 2012 Red Hat Inc.\n" + "Usage:\n" + " %s [--options] -s domname -d domname path\n" + "Options:\n" + " -c|--connect uri Specify libvirt URI for -s and -d options\n" + " -s|--seed guest Add seed disk from libvirt guest\n" + " -d|--domain guest Add target disk from libvirt guest\n" + " --keys-from-stdin Read passphrases from stdin\n" + " --echo-keys Don't turn off echo for passphrases\n" + " --help Display brief help\n"), + program_name, program_name); + } + exit (status); +} + +/* Make a LUKS map name from the partition name, + * eg "/dev/vda2" => "luksvda2" + */ +static void +diff_make_mapname (const char *device, char *mapname, size_t len) +{ + size_t i = 0; + + if (len < 5) + abort (); + strcpy (mapname, "luks"); + mapname += 4; + len -= 4; + + if (STRPREFIX (device, "/dev/")) + i = 5; + + for (; device[i] != '\0' && len >= 1; ++i) { + if (c_isalnum (device[i])) { + *mapname++ = device[i]; + len--; + } + } + + *mapname = '\0'; +} + +static void +free_strings (char **strings) +{ + size_t i; + + for (i = 0; strings[i] != NULL; ++i) + free (strings[i]); + free (strings); +} + +static size_t +count_strings (char **strings) +{ + size_t i; + + for (i = 0; strings[i] != NULL; ++i) + ; + return i; +} + +/* 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]; + diff_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 int +compare_keys (const void *p1, const void *p2) +{ + const char *key1 = * (char * const *) p1; + const char *key2 = * (char * const *) p2; + + return strcmp (key1, key2); +} + +static int +compare_keys_len (const void *p1, const void *p2) +{ + const char *key1 = * (char * const *) p1; + const char *key2 = * (char * const *) p2; + int c; + + c = strlen (key1) - strlen (key2); + if (c != 0) + return c; + + return compare_keys (p1, p2); +} + +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) +{ + const char *root = NULL; + 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 (g, root); +} + +static void +free_drive (struct drv *drv) +{ + if (!drv) return; + + free (drv->device); + free (drv); +} + +int +main (int argc, char *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'}, + {"help", 0, 0, HELP_OPTION}, + {0, 0, 0, 0} + }; + + struct drv *sdrv = NULL; + struct drv *ddrv = NULL; + int c, nr; + int option_index; + int spid, dpid; + + sg = guestfs_create (); + if (sg == NULL) { + fprintf (stderr, _("guestfs_create: failed to create seed handle\n")); + exit (EXIT_FAILURE); + } + + dg = guestfs_create (); + if (dg == NULL) { + fprintf (stderr, _("guestfs_create: failed to create comparison handle\n")); + exit (EXIT_FAILURE); + } + + argv[0] = diff_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, "keys-from-stdin")) { + keys_from_stdin = 1; + } else if (STREQ (long_options[option_index].name, "echo-keys")) { + echo_keys = 1; + } else if (STREQ (long_options[option_index].name, "seed")) { + if (sdrv) { + fprintf(stderr, _("Only one seed device")); + 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")) { + if (ddrv) { + fprintf (stderr, _("Only one diff device")); + exit (EXIT_FAILURE); + } + ddrv = calloc (1, sizeof (struct drv)); + if (!ddrv) { + perror ("malloc"); + exit (EXIT_FAILURE); + } + ddrv->type = drv_d; + ddrv->nr_drives = -1; + ddrv->d.guest = optarg; + ddrv->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 'c': + libvirt_uri = optarg; + + case 's': + if (sdrv) { + fprintf(stderr, _("Only one seed device")); + 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 'd': + if (ddrv) { + fprintf (stderr, _("Only one diff device")); + exit (EXIT_FAILURE); + } + ddrv = calloc (1, sizeof (struct drv)); + if (!ddrv) { + perror ("malloc"); + exit (EXIT_FAILURE); + } + ddrv->type = drv_d; + ddrv->nr_drives = -1; + ddrv->d.guest = optarg; + ddrv->next = NULL; + break; + + case HELP_OPTION: + diff_usage (EXIT_SUCCESS); + + default: + diff_usage (EXIT_FAILURE); + } + } + + if (sdrv == NULL) + diff_usage (EXIT_FAILURE); + if (ddrv == NULL) + diff_usage (EXIT_FAILURE); + + struct guestfs_add_domain_argv optargs = { + .bitmask = 0, + .libvirturi = libvirt_uri, + .readonly = 1, + .allowuuid = 1, + .readonlydisk = "read", + }; + nr = guestfs_add_domain_argv (sg, sdrv->d.guest, &optargs); + if (nr == -1) + exit (EXIT_FAILURE); + sdrv->nr_drives = nr; + nr = guestfs_add_domain_argv (dg, ddrv->d.guest, &optargs); + if (nr == -1) + exit (EXIT_FAILURE); + ddrv->nr_drives = nr; + + if (guestfs_launch (sg) == -1) + exit (EXIT_FAILURE); + if (guestfs_launch (dg) == -1) + exit (EXIT_FAILURE); + + diff_inspect_mount (sg); + diff_inspect_mount (dg); + + char stempdir[] = "/tmp/sGuestXXXXXX"; + char dtempdir[] = "/tmp/dGuestXXXXXX"; + + if (mkdtemp (stempdir) == NULL) { + perror ("mkdtemp"); + exit (EXIT_FAILURE); + } + if (mkdtemp (dtempdir) == NULL) { + perror ("mkdtemp"); + exit (EXIT_FAILURE); + } + + if (guestfs_mount_local (sg, stempdir, -1) == -1) + exit (EXIT_FAILURE); + if (guestfs_mount_local (dg, dtempdir, -1) == -1) + exit (EXIT_FAILURE); + + spid = fork (); + if (spid == -1) { + perror ("fork"); + exit (EXIT_FAILURE); + } + if (spid == 0) { + if (guestfs_mount_local_run (sg) == -1) + exit (EXIT_FAILURE); + _exit (EXIT_SUCCESS); + } + + dpid = fork(); + if (dpid == -1) { + perror ("fork"); + exit (EXIT_FAILURE); + } + if (dpid == 0) { + if (guestfs_mount_local_run (dg) == -1) + exit (EXIT_FAILURE); + _exit (EXIT_SUCCESS); + } + + const char *dir = argv[optind]; + char system_arg[BUFSIZ]; + sprintf (system_arg, "diff -urN %s%s %s%s", stempdir, dir, + dtempdir, dir); + sleep (5); + if (system (system_arg) == -1) + exit (EXIT_FAILURE); + + guestfs_umount_local (sg, GUESTFS_UMOUNT_LOCAL_RETRY, 1, -1); + waitpid (spid, NULL, 0); + guestfs_umount_local (dg, GUESTFS_UMOUNT_LOCAL_RETRY, 1, -1); + waitpid (dpid, NULL, 0); + + if (guestfs_shutdown (sg) == -1) + exit (EXIT_FAILURE); + if (guestfs_shutdown (dg) == -1) + exit (EXIT_FAILURE); + + free_drive (sdrv); + free_drive (ddrv); + + guestfs_close (sg); + guestfs_close (dg); + + exit (EXIT_SUCCESS); +} diff --git a/po/POTFILES b/po/POTFILES index dffa899..3f51648 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -1,6 +1,7 @@ align/domains.c align/scan.c cat/virt-cat.c +cat/virt-diff.c cat/virt-filesystems.c cat/virt-ls.c daemon/9p.c -- 1.7.12.rc1
Wanlong Gao
2012-Aug-20 08:22 UTC
[Libguestfs] [PATCH V3] virt-diff: add new virt-diff tool
add new virt-diff tool Signed-off-by: Wanlong Gao <gaowanlong at cn.fujitsu.com> --- cat/Makefile.am | 20 ++- cat/virt-diff.c | 525 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ po/POTFILES | 1 + 3 files changed, 545 insertions(+), 1 deletion(-) create mode 100644 cat/virt-diff.c diff --git a/cat/Makefile.am b/cat/Makefile.am index f7c763a..5f6a986 100644 --- a/cat/Makefile.am +++ b/cat/Makefile.am @@ -27,7 +27,7 @@ EXTRA_DIST = \ CLEANFILES = stamp-virt-cat.pod stamp-virt-ls.pod stamp-virt-filesystems.pod -bin_PROGRAMS = virt-cat virt-filesystems virt-ls +bin_PROGRAMS = virt-cat virt-filesystems virt-ls virt-diff SHARED_SOURCE_FILES = \ ../fish/config.c \ @@ -91,6 +91,24 @@ virt_ls_LDADD = \ $(top_builddir)/src/libguestfs.la \ ../gnulib/lib/libgnu.la +virt_diff_SOURCES = \ + ../fish/keys.c \ + virt-diff.c + +virt_diff_CFLAGS = \ + -DGUESTFS_WARN_DEPRECATED=1 \ + -I$(top_srcdir)/src -I$(top_builddir)/src \ + -I$(top_srcdir)/fish \ + -I$(srcdir)/../gnulib/lib -I../gnulib/lib \ + -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ + $(LIBCONFIG_CFLAGS) + +virt_diff_LDADD = \ + $(LIBCONFIG_LIBS) \ + $(top_builddir)/src/libguestfs.la \ + ../gnulib/lib/libgnu.la + # Manual pages and HTML files for the website. man_MANS = virt-cat.1 virt-filesystems.1 virt-ls.1 diff --git a/cat/virt-diff.c b/cat/virt-diff.c new file mode 100644 index 0000000..c2c59a6 --- /dev/null +++ b/cat/virt-diff.c @@ -0,0 +1,525 @@ +/* virt-diff + * Copyright (C) 2012 Fujitsu Limited. + * 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 <termios.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 <sys/wait.h> + +#include "c-ctype.h" + +#include "human.h" +#include "progname.h" + +#include "guestfs.h" +#include "options.h" + +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + +int keys_from_stdin = 0; +int echo_keys = 0; +/* libguestfs handle for seed. */ +static guestfs_h *sg; + +/* libguestfs handle for temp. */ +static guestfs_h *dg; + +const char *libvirt_uri = NULL; + +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) 2012 Red Hat Inc.\n" + "Usage:\n" + " %s [--options] -s domname -d domname path\n" + "Options:\n" + " -c|--connect uri Specify libvirt URI for -s and -d options\n" + " -s|--seed guest Add seed disk from libvirt guest\n" + " -d|--domain guest Add target disk from libvirt guest\n" + " --keys-from-stdin Read passphrases from stdin\n" + " --echo-keys Don't turn off echo for passphrases\n" + " --help Display brief help\n"), + program_name, program_name); + } + exit (status); +} + +/* Make a LUKS map name from the partition name, + * eg "/dev/vda2" => "luksvda2" + */ +static void +diff_make_mapname (const char *device, char *mapname, size_t len) +{ + size_t i = 0; + + if (len < 5) + abort (); + strcpy (mapname, "luks"); + mapname += 4; + len -= 4; + + if (STRPREFIX (device, "/dev/")) + i = 5; + + for (; device[i] != '\0' && len >= 1; ++i) { + if (c_isalnum (device[i])) { + *mapname++ = device[i]; + len--; + } + } + + *mapname = '\0'; +} + +static void +free_strings (char **strings) +{ + size_t i; + + for (i = 0; strings[i] != NULL; ++i) + free (strings[i]); + free (strings); +} + +static size_t +count_strings (char **strings) +{ + size_t i; + + for (i = 0; strings[i] != NULL; ++i) + ; + return i; +} + +/* 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]; + diff_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 int +compare_keys (const void *p1, const void *p2) +{ + const char *key1 = * (char * const *) p1; + const char *key2 = * (char * const *) p2; + + return strcmp (key1, key2); +} + +static int +compare_keys_len (const void *p1, const void *p2) +{ + const char *key1 = * (char * const *) p1; + const char *key2 = * (char * const *) p2; + int c; + + c = strlen (key1) - strlen (key2); + if (c != 0) + return c; + + return compare_keys (p1, p2); +} + +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) +{ + const char *root = NULL; + 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 (g, root); +} + +static void +free_drive (struct drv *drv) +{ + if (!drv) return; + + free (drv->device); + free (drv); +} + +int +main (int argc, char *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'}, + {"help", 0, 0, HELP_OPTION}, + {0, 0, 0, 0} + }; + + struct drv *sdrv = NULL; + struct drv *ddrv = NULL; + int c, nr; + int option_index; + int spid, dpid; + + sg = guestfs_create (); + if (sg == NULL) { + fprintf (stderr, _("guestfs_create: failed to create seed handle\n")); + exit (EXIT_FAILURE); + } + + dg = guestfs_create (); + if (dg == NULL) { + fprintf (stderr, _("guestfs_create: failed to create comparison handle\n")); + exit (EXIT_FAILURE); + } + + argv[0] = diff_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, "keys-from-stdin")) { + keys_from_stdin = 1; + } else if (STREQ (long_options[option_index].name, "echo-keys")) { + echo_keys = 1; + } else if (STREQ (long_options[option_index].name, "seed")) { + if (sdrv) { + fprintf(stderr, _("Only one seed device")); + 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")) { + if (ddrv) { + fprintf (stderr, _("Only one diff device")); + exit (EXIT_FAILURE); + } + ddrv = calloc (1, sizeof (struct drv)); + if (!ddrv) { + perror ("malloc"); + exit (EXIT_FAILURE); + } + ddrv->type = drv_d; + ddrv->nr_drives = -1; + ddrv->d.guest = optarg; + ddrv->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 'c': + libvirt_uri = optarg; + + case 's': + if (sdrv) { + fprintf(stderr, _("Only one seed device")); + 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 'd': + if (ddrv) { + fprintf (stderr, _("Only one diff device")); + exit (EXIT_FAILURE); + } + ddrv = calloc (1, sizeof (struct drv)); + if (!ddrv) { + perror ("malloc"); + exit (EXIT_FAILURE); + } + ddrv->type = drv_d; + ddrv->nr_drives = -1; + ddrv->d.guest = optarg; + ddrv->next = NULL; + break; + + case HELP_OPTION: + diff_usage (EXIT_SUCCESS); + + default: + diff_usage (EXIT_FAILURE); + } + } + + if (sdrv == NULL) + diff_usage (EXIT_FAILURE); + if (ddrv == NULL) + diff_usage (EXIT_FAILURE); + + struct guestfs_add_domain_argv optargs = { + .bitmask = 0, + .libvirturi = libvirt_uri, + .readonly = 1, + .allowuuid = 1, + .readonlydisk = "read", + }; + nr = guestfs_add_domain_argv (sg, sdrv->d.guest, &optargs); + if (nr == -1) + exit (EXIT_FAILURE); + sdrv->nr_drives = nr; + nr = guestfs_add_domain_argv (dg, ddrv->d.guest, &optargs); + if (nr == -1) + exit (EXIT_FAILURE); + ddrv->nr_drives = nr; + + if (guestfs_launch (sg) == -1) + exit (EXIT_FAILURE); + if (guestfs_launch (dg) == -1) + exit (EXIT_FAILURE); + + diff_inspect_mount (sg); + diff_inspect_mount (dg); + + char stempdir[] = "/tmp/sGuestXXXXXX"; + char dtempdir[] = "/tmp/dGuestXXXXXX"; + + if (mkdtemp (stempdir) == NULL) { + perror ("mkdtemp"); + exit (EXIT_FAILURE); + } + if (mkdtemp (dtempdir) == NULL) { + perror ("mkdtemp"); + exit (EXIT_FAILURE); + } + + if (guestfs_mount_local (sg, stempdir, -1) == -1) + exit (EXIT_FAILURE); + if (guestfs_mount_local (dg, dtempdir, -1) == -1) + exit (EXIT_FAILURE); + + spid = fork (); + if (spid == -1) { + perror ("fork"); + exit (EXIT_FAILURE); + } + if (spid == 0) { + if (guestfs_mount_local_run (sg) == -1) + exit (EXIT_FAILURE); + _exit (EXIT_SUCCESS); + } + + dpid = fork(); + if (dpid == -1) { + perror ("fork"); + exit (EXIT_FAILURE); + } + if (dpid == 0) { + if (guestfs_mount_local_run (dg) == -1) + exit (EXIT_FAILURE); + _exit (EXIT_SUCCESS); + } + + const char *dir = argv[optind]; + char system_arg[BUFSIZ]; + sprintf (system_arg, "diff -urN %s%s %s%s", stempdir, dir, + dtempdir, dir); + sleep (5); + if (system (system_arg) == -1) + exit (EXIT_FAILURE); + + guestfs_umount_local (sg, GUESTFS_UMOUNT_LOCAL_RETRY, 1, -1); + waitpid (spid, NULL, 0); + guestfs_umount_local (dg, GUESTFS_UMOUNT_LOCAL_RETRY, 1, -1); + waitpid (dpid, NULL, 0); + + if (guestfs_shutdown (sg) == -1) + exit (EXIT_FAILURE); + if (guestfs_shutdown (dg) == -1) + exit (EXIT_FAILURE); + + free_drive (sdrv); + free_drive (ddrv); + + guestfs_close (sg); + guestfs_close (dg); + + exit (EXIT_SUCCESS); +} diff --git a/po/POTFILES b/po/POTFILES index 60887dc..ad58555 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -1,6 +1,7 @@ align/domains.c align/scan.c cat/virt-cat.c +cat/virt-diff.c cat/virt-filesystems.c cat/virt-ls.c daemon/9p.c -- 1.7.12
Seemingly Similar Threads
- [PATCH] common/options: Change drv struct to store drive index instead of device name.
- [PATCH 1/2] Use 'error' function consistently throughout.
- [PATCH] error log: keep more calloc and its error messages match
- [PATCH 1/3] fish: move disk decryption helpers in own file
- [PATCH 2/2] options: Allow multiple --key parameters and default keys.