[Libguestfs] [RFC] virt-diff: add new virt-diff tool

Wanlong Gao gaowanlong at cn.fujitsu.com
Tue Jun 19 09:24:26 UTC 2012


add new virt-diff tool

Signed-off-by: Wanlong Gao <gaowanlong at 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.


Thanks,
Wanlong Gao


 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}
+  };
+
+  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);
+  }
+
+  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




More information about the Libguestfs mailing list