[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[Libguestfs] [PATCH 2/2] example: Copying a directory between two guests using threads.



From: "Richard W.M. Jones" <rjones redhat com>

---
 .gitignore           |    1 +
 examples/Makefile.am |   16 +++
 examples/copy_over.c |  255 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 272 insertions(+), 0 deletions(-)
 create mode 100644 examples/copy_over.c

diff --git a/.gitignore b/.gitignore
index 3e37163..f6c42a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -73,6 +73,7 @@ erlang/examples/guestfs-erlang.3
 erlang/examples/stamp-guestfs-erlang.pod
 erlang/guestfs.beam
 erlang/guestfs.erl
+examples/copy_over
 examples/create_disk
 examples/display_icon
 examples/guestfs-examples.3
diff --git a/examples/Makefile.am b/examples/Makefile.am
index ef4a581..137dfe6 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -27,10 +27,26 @@ CLEANFILES = \
 	stamp-guestfs-testing.pod
 
 noinst_PROGRAMS = create_disk display_icon inspect_vm
+if HAVE_LIBVIRT
+noinst_PROGRAMS += copy_over
+endif
 if HAVE_HIVEX
 noinst_PROGRAMS += virt-dhcp-address
 endif
 
+if HAVE_LIBVIRT
+copy_over_SOURCES = copy_over.c
+copy_over_CFLAGS = \
+	-DGUESTFS_WARN_DEPRECATED=1 \
+	-I$(top_srcdir)/src -I$(top_builddir)/src \
+	$(LIBVIRT_CFLAGS) \
+	-pthread \
+	$(WARN_CFLAGS) $(WERROR_CFLAGS)
+copy_over_LDADD = \
+	$(top_builddir)/src/libguestfs.la \
+	$(LIBVIRT_LIBS)
+endif
+
 create_disk_SOURCES = create_disk.c
 create_disk_CFLAGS = \
 	-DGUESTFS_WARN_DEPRECATED=1 \
diff --git a/examples/copy_over.c b/examples/copy_over.c
new file mode 100644
index 0000000..e3d1865
--- /dev/null
+++ b/examples/copy_over.c
@@ -0,0 +1,255 @@
+/* Copy a directory from one libvirt guest to another.  This
+ * demonstrates using multiple handles with threads, and connecting a
+ * pipe from one handle to another.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <pthread.h>
+
+#include <guestfs.h>
+#include <libvirt/libvirt.h>
+
+struct threaddata {
+  const char *src;
+  const char *srcdir;
+  int fd;
+  pthread_t mainthread;
+};
+
+static void *start_srcthread (void *);
+static int open_guest (guestfs_h *g, const char *dom, int readonly);
+
+static void
+usage (void)
+{
+  fprintf (stderr,
+    "Usage: copy_over source srcdir dest destdir\n"
+    "\n"
+    "  source  : the source domain (a libvirt guest name)\n"
+    "  srcdir  : the directory to copy from the source guest\n"
+    "  dest    : the destination domain (a libvirt guest name)\n"
+    "  destdir : the destination directory (must exist at destination)\n"
+    "\n"
+    "eg: copy_over Src /home/rjones Dest /tmp\n"
+    "would copy /home/rjones from Src to /tmp/rjones on Dest\n"
+    "\n"
+    "The destination guest cannot be running.\n");
+}
+
+int
+main (int argc, char *argv[])
+{
+  const char *src, *srcdir, *dest, *destdir;
+  guestfs_h *destg;
+  int fd[2];
+  pthread_t srcthread;
+  struct threaddata threaddata;
+  int err;
+  char fdname[128];
+
+  /* This is required when using libvirt from multiple threads. */
+  virInitialize ();
+
+  if (argc != 5) {
+    usage ();
+    exit (EXIT_FAILURE);
+  }
+
+  src = argv[1];
+  srcdir = argv[2];
+  dest = argv[3];
+  destdir = argv[4];
+
+  /* Instead of downloading to local disk and uploading, we are going
+   * to connect the source download and destination upload using a
+   * pipe.  Create that pipe.
+   */
+  if (pipe (fd) == -1) {
+    perror ("pipe");
+    exit (EXIT_FAILURE);
+  }
+
+  /* We don't want the pipe to be passed to / held open in subprocesses. */
+  if (fcntl (fd[0], F_SETFD, FD_CLOEXEC) == -1 ||
+      fcntl (fd[1], F_SETFD, FD_CLOEXEC) == -1) {
+    perror ("fcntl");
+    exit (EXIT_FAILURE);
+  }
+
+  /* The libguestfs API is synchronous, so if we want to use two
+   * handles concurrently, then we have to have two threads.  In this
+   * case the main thread (this one) is handling the destination
+   * domain (uploading), and we create one more thread to handle the
+   * source domain (downloading).
+   */
+  threaddata.src = src;
+  threaddata.srcdir = srcdir;
+  threaddata.fd = fd[1];
+  threaddata.mainthread = pthread_self ();
+  err = pthread_create (&srcthread, NULL, start_srcthread, &threaddata);
+  if (err != 0) {
+    fprintf (stderr, "pthread_create: %s\n", strerror (err));
+    exit (EXIT_FAILURE);
+  }
+
+  /* Open the destination domain. */
+  destg = guestfs_create ();
+  if (!destg) {
+    perror ("failed to create libguestfs handle");
+    pthread_cancel (srcthread);
+    exit (EXIT_FAILURE);
+  }
+  if (open_guest (destg, dest, 0) == -1) {
+    pthread_cancel (srcthread);
+    exit (EXIT_FAILURE);
+  }
+
+  /* Begin the upload. */
+  snprintf (fdname, sizeof fdname, "/dev/fd/%d", fd[0]);
+  if (guestfs_tar_in (destg, fdname, destdir) == -1) {
+    pthread_cancel (srcthread);
+    exit (EXIT_FAILURE);
+  }
+
+  /* Close our end of the pipe.  The other thread will close the
+   * other side of the pipe.
+   */
+  close (fd[0]);
+
+  /* Wait for the other thread to finish. */
+  err = pthread_join (srcthread, NULL);
+  if (err != 0) {
+    fprintf (stderr, "pthread_join: %s\n", strerror (err));
+    exit (EXIT_FAILURE);
+  }
+
+  /* Clean up. */
+  if (guestfs_umount_all (destg) == -1)
+    exit (EXIT_FAILURE);
+  guestfs_close (destg);
+
+  exit (EXIT_SUCCESS);
+}
+
+static void *
+start_srcthread (void *arg)
+{
+  struct threaddata *threaddata = arg;
+  guestfs_h *srcg;
+  char fdname[128];
+
+  /* Open the source domain. */
+  srcg = guestfs_create ();
+  if (!srcg) {
+    perror ("failed to create libguestfs handle");
+    pthread_cancel (threaddata->mainthread);
+    exit (EXIT_FAILURE);
+  }
+  if (open_guest (srcg, threaddata->src, 1) == -1) {
+    pthread_cancel (threaddata->mainthread);
+    exit (EXIT_FAILURE);
+  }
+
+  /* Begin the download. */
+  snprintf (fdname, sizeof fdname, "/dev/fd/%d", threaddata->fd);
+  if (guestfs_tar_out (srcg, threaddata->srcdir, fdname) == -1) {
+    pthread_cancel (threaddata->mainthread);
+    exit (EXIT_FAILURE);
+  }
+
+  /* Close the pipe; this will cause the receiver to finish the upload. */
+  if (close (threaddata->fd) == -1) {
+    pthread_cancel (threaddata->mainthread);
+    exit (EXIT_FAILURE);
+  }
+
+  /* Clean up. */
+  if (guestfs_umount_all (srcg) == -1) {
+    pthread_cancel (threaddata->mainthread);
+    exit (EXIT_FAILURE);
+  }
+  guestfs_close (srcg);
+
+  return NULL;
+}
+
+static int
+compare_keys_len (const void *p1, const void *p2)
+{
+  const char *key1 = * (char * const *) p1;
+  const char *key2 = * (char * const *) p2;
+  return strlen (key1) - strlen (key2);
+}
+
+static size_t
+count_strings (char *const *argv)
+{
+  size_t c;
+
+  for (c = 0; argv[c]; ++c)
+    ;
+  return c;
+}
+
+/* This function deals with the complexity of adding the domain,
+ * launching the handle, and mounting up filesystems.  See
+ * 'examples/inspect_vm.c' to understand how this works.
+ */
+static int
+open_guest (guestfs_h *g, const char *dom, int readonly)
+{
+  char **roots, *root, **mountpoints;
+  size_t i;
+
+  /* Use libvirt to find the guest disks and add them to the handle. */
+  if (guestfs_add_domain (g, dom,
+                          GUESTFS_ADD_DOMAIN_READONLY, readonly,
+                          -1) == -1)
+    return -1;
+
+  if (guestfs_launch (g) == -1)
+    return -1;
+
+  /* Inspect the guest, looking for operating systems. */
+  roots = guestfs_inspect_os (g);
+  if (roots == NULL)
+    return -1;
+
+  if (roots[0] == NULL || roots[1] != NULL) {
+    fprintf (stderr, "copy_over: %s: no operating systems or multiple operating systems found\n", dom);
+    return -1;
+  }
+
+  root = roots[0];
+
+  /* Mount up the filesystems (like 'guestfish -i'). */
+  mountpoints = guestfs_inspect_get_mountpoints (g, root);
+  if (mountpoints == NULL)
+    return -1;
+
+  qsort (mountpoints, count_strings (mountpoints) / 2, 2 * sizeof (char *),
+         compare_keys_len);
+  for (i = 0; mountpoints[i] != NULL; i += 2) {
+    /* Ignore failures from this call, since bogus entries can
+     * appear in the guest's /etc/fstab.
+     */
+    (readonly ? guestfs_mount_ro : guestfs_mount)
+      (g, mountpoints[i+1], mountpoints[i]);
+    free (mountpoints[i]);
+    free (mountpoints[i+1]);
+  }
+
+  free (mountpoints);
+
+  free (root);
+  free (roots);
+
+  /* Everything ready, no error. */
+  return 0;
+}
-- 
1.7.9.1


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]