From: "Richard W.M. Jones" <rjones(a)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