table of contents
- NAME
- SYNOPSIS
- DESCRIPTION
- EXAMPLE: CREATE A DISK IMAGE
- EXAMPLE: INSPECT A VIRTUAL MACHINE DISK IMAGE
- EXAMPLE: ENABLE DEBUGGING AND LOGGING
- EXAMPLE: DISPLAY THE OPERATING SYSTEM ICON OF A GUEST
- EXAMPLE: THE LIBVIRT AUTHENTICATION API
- EXAMPLE: THE MOUNT LOCAL API
- EXAMPLE: MULTIPLE HANDLES AND THREADS
- EXAMPLE: FETCH DHCP ADDRESS FROM A GUEST
- SEE ALSO
- AUTHORS
- COPYRIGHT
- LICENSE
- BUGS
guestfs-examples(3) | Virtualization Support | guestfs-examples(3) |
NAME¶
guestfs-examples - Examples of using libguestfs from C
SYNOPSIS¶
#include <guestfs.h> guestfs_h *g = guestfs_create (); guestfs_add_drive_ro (g, "disk.img"); guestfs_launch (g); cc prog.c -o prog -lguestfs or: cc prog.c -o prog `pkg-config libguestfs --cflags --libs`
DESCRIPTION¶
This manual page contains examples of calling libguestfs from the C programming language. If you are not familiar with using libguestfs, you also need to read guestfs(3).
EXAMPLE: CREATE A DISK IMAGE¶
/* Example showing how to create a disk image. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <guestfs.h> int main (int argc, char *argv[]) { guestfs_h *g; size_t i; g = guestfs_create (); if (g == NULL) { perror ("failed to create libguestfs handle"); exit (EXIT_FAILURE); } /* Set the trace flag so that we can see each libguestfs call. */ guestfs_set_trace (g, 1); /* Create a raw-format sparse disk image, 512 MB in size. */ if (guestfs_disk_create (g, "disk.img", "raw", UINT64_C(512)*1024*1024, -1) == -1) exit (EXIT_FAILURE); /* Add the disk image to libguestfs. */ if (guestfs_add_drive_opts (g, "disk.img", GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw", /* raw format */ GUESTFS_ADD_DRIVE_OPTS_READONLY, 0, /* for write */ -1) /* this marks end of optional arguments */ == -1) exit (EXIT_FAILURE); /* Run the libguestfs back-end. */ if (guestfs_launch (g) == -1) exit (EXIT_FAILURE); /* Get the list of devices. Because we only added one drive * above, we expect that this list should contain a single * element. */ char **devices = guestfs_list_devices (g); if (devices == NULL) exit (EXIT_FAILURE); if (devices[0] == NULL || devices[1] != NULL) { fprintf (stderr, "error: expected a single device from list-devices\n"); exit (EXIT_FAILURE); } /* Partition the disk as one single MBR partition. */ if (guestfs_part_disk (g, devices[0], "mbr") == -1) exit (EXIT_FAILURE); /* Get the list of partitions. We expect a single element, which * is the partition we have just created. */ char **partitions = guestfs_list_partitions (g); if (partitions == NULL) exit (EXIT_FAILURE); if (partitions[0] == NULL || partitions[1] != NULL) { fprintf (stderr, "error: expected a single partition from list-partitions\n"); exit (EXIT_FAILURE); } /* Create a filesystem on the partition. */ if (guestfs_mkfs (g, "ext4", partitions[0]) == -1) exit (EXIT_FAILURE); /* Now mount the filesystem so that we can add files. */ if (guestfs_mount (g, partitions[0], "/") == -1) exit (EXIT_FAILURE); /* Create some files and directories. */ if (guestfs_touch (g, "/empty") == -1) exit (EXIT_FAILURE); const char *message = "Hello, world\n"; if (guestfs_write (g, "/hello", message, strlen (message)) == -1) exit (EXIT_FAILURE); if (guestfs_mkdir (g, "/foo") == -1) exit (EXIT_FAILURE); /* This one uploads the local file /etc/resolv.conf into * the disk image. */ if (guestfs_upload (g, "/etc/resolv.conf", "/foo/resolv.conf") == -1) exit (EXIT_FAILURE); /* Because we wrote to the disk and we want to detect write * errors, call guestfs_shutdown. You don't need to do this: * guestfs_close will do it implicitly. */ if (guestfs_shutdown (g) == -1) exit (EXIT_FAILURE); guestfs_close (g); /* Free up the lists. */ for (i = 0; devices[i] != NULL; ++i) free (devices[i]); free (devices); for (i = 0; partitions[i] != NULL; ++i) free (partitions[i]); free (partitions); exit (EXIT_SUCCESS); }
EXAMPLE: INSPECT A VIRTUAL MACHINE DISK IMAGE¶
/* Inspect a disk image and display operating systems it may contain. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <guestfs.h> 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; } int main (int argc, char *argv[]) { guestfs_h *g; const char *disk; char **roots, *root, *str, **mountpoints, **lines; size_t i, j; if (argc != 2) { fprintf (stderr, "usage: inspect_vm disk.img\n"); exit (EXIT_FAILURE); } disk = argv[1]; g = guestfs_create (); if (g == NULL) { perror ("failed to create libguestfs handle"); exit (EXIT_FAILURE); } /* Attach the disk image read-only to libguestfs. */ if (guestfs_add_drive_opts (g, disk, /* GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw", */ GUESTFS_ADD_DRIVE_OPTS_READONLY, 1, -1) /* this marks end of optional arguments */ == -1) exit (EXIT_FAILURE); /* Run the libguestfs back-end. */ if (guestfs_launch (g) == -1) exit (EXIT_FAILURE); /* Ask libguestfs to inspect for operating systems. */ roots = guestfs_inspect_os (g); if (roots == NULL) exit (EXIT_FAILURE); if (roots[0] == NULL) { fprintf (stderr, "inspect_vm: no operating systems found\n"); exit (EXIT_FAILURE); } for (j = 0; roots[j] != NULL; ++j) { root = roots[j]; printf ("Root device: %s\n", root); /* Print basic information about the operating system. */ str = guestfs_inspect_get_product_name (g, root); if (str) printf (" Product name: %s\n", str); free (str); printf (" Version: %d.%d\n", guestfs_inspect_get_major_version (g, root), guestfs_inspect_get_minor_version (g, root)); str = guestfs_inspect_get_type (g, root); if (str) printf (" Type: %s\n", str); free (str); str = guestfs_inspect_get_distro (g, root); if (str) printf (" Distro: %s\n", str); free (str); /* Mount up the disks, like guestfish -i. * * Sort keys by length, shortest first, so that we end up * mounting the filesystems in the correct order. */ mountpoints = guestfs_inspect_get_mountpoints (g, root); if (mountpoints == NULL) exit (EXIT_FAILURE); 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. */ guestfs_mount_ro (g, mountpoints[i+1], mountpoints[i]); free (mountpoints[i]); free (mountpoints[i+1]); } free (mountpoints); /* If /etc/issue.net file exists, print up to 3 lines. */ if (guestfs_is_file (g, "/etc/issue.net") > 0) { printf ("--- /etc/issue.net ---\n"); lines = guestfs_head_n (g, 3, "/etc/issue.net"); if (lines == NULL) exit (EXIT_FAILURE); for (i = 0; lines[i] != NULL; ++i) { printf ("%s\n", lines[i]); free (lines[i]); } free (lines); } /* Unmount everything. */ if (guestfs_umount_all (g) == -1) exit (EXIT_FAILURE); free (root); } free (roots); guestfs_close (g); exit (EXIT_SUCCESS); }
EXAMPLE: ENABLE DEBUGGING AND LOGGING¶
/* Example showing how to enable debugging, and capture it into any * custom logging system (syslog in this example, but any could be * used). Note this uses the event API which is also available in * non-C language bindings. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <syslog.h> #include <guestfs.h> static void message_callback (guestfs_h *g, void *opaque, uint64_t event, int event_handle, int flags, const char *buf, size_t buf_len, const uint64_t *array, size_t array_len); /* Events we are interested in. This bitmask covers all trace and * debug messages. */ static const uint64_t event_bitmask = GUESTFS_EVENT_LIBRARY | GUESTFS_EVENT_WARNING | GUESTFS_EVENT_APPLIANCE | GUESTFS_EVENT_TRACE; int main (int argc, char *argv[]) { guestfs_h *g; g = guestfs_create (); if (g == NULL) { perror ("failed to create libguestfs handle"); exit (EXIT_FAILURE); } /* By default, debugging information is printed on stderr. To * capture it somewhere else you have to set up an event handler * which will be called back as debug messages are generated. To do * this use the event API. * * For more information see EVENTS in guestfs(3). */ if (guestfs_set_event_callback (g, message_callback, event_bitmask, 0, NULL) == -1) exit (EXIT_FAILURE); /* This is how debugging is enabled: * * Setting the 'trace' flag in the handle means that each libguestfs * call is logged (name, parameters, return). This flag is useful * to see how libguestfs is being used by a program. * * Setting the 'verbose' flag enables a great deal of extra * debugging throughout the system. This is useful if there is a * libguestfs error which you don't understand. * * Note that you should set the flags early on after creating the * handle. In particular if you set the verbose flag after launch * then you won't see all messages. * * For more information see: * http://libguestfs.org/guestfs-faq.1.html#debugging-libguestfs * * Error messages raised by APIs are *not* debugging information, * and they are not affected by any of this. You may have to log * them separately. */ guestfs_set_trace (g, 1); guestfs_set_verbose (g, 1); /* Do some operations which will generate plenty of trace and debug * messages. */ if (guestfs_add_drive (g, "/dev/null") == -1) exit (EXIT_FAILURE); printf ("There is no output from this program. " "Take a look in your system log file,\n" "eg. /var/log/messages.\n"); if (guestfs_launch (g) == -1) exit (EXIT_FAILURE); guestfs_close (g); exit (EXIT_SUCCESS); } /* This function is called back by libguestfs whenever a trace or * debug message is generated. * * For the classes of events we have registered above, 'array' and * 'array_len' will not be meaningful. Only 'buf' and 'buf_len' will * be interesting and these will contain the trace or debug message. * * This example simply redirects these messages to syslog, but * obviously you could do something more advanced here. */ static void message_callback (guestfs_h *g, void *opaque, uint64_t event, int event_handle, int flags, const char *buf, size_t buf_len, const uint64_t *array, size_t array_len) { const int priority = LOG_USER|LOG_INFO; char *event_name, *msg; if (buf_len > 0) { event_name = guestfs_event_to_string (event); msg = strndup (buf, buf_len); syslog (priority, "[%s] %s", event_name, msg); free (msg); free (event_name); } }
EXAMPLE: DISPLAY THE OPERATING SYSTEM ICON OF A GUEST¶
/* This example inspects a guest using libguestfs inspection (see * "INSPECTION" in guestfs(3)), and if possible displays a * representative icon or logo for the guest's operating system. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <guestfs.h> 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; } int main (int argc, char *argv[]) { guestfs_h *g; const char *disk; char **roots, *root, **mountpoints, *icon; size_t i, j, icon_size; FILE *fp; if (argc != 2) { fprintf (stderr, "usage: display-icon disk.img\n"); exit (EXIT_FAILURE); } disk = argv[1]; g = guestfs_create (); if (g == NULL) { perror ("failed to create libguestfs handle"); exit (EXIT_FAILURE); } /* Attach the disk image read-only to libguestfs. */ if (guestfs_add_drive_opts (g, disk, /* GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw", */ GUESTFS_ADD_DRIVE_OPTS_READONLY, 1, -1) /* this marks end of optional arguments */ == -1) exit (EXIT_FAILURE); /* Run the libguestfs back-end. */ if (guestfs_launch (g) == -1) exit (EXIT_FAILURE); /* Ask libguestfs to inspect for operating systems. */ roots = guestfs_inspect_os (g); if (roots == NULL) exit (EXIT_FAILURE); if (roots[0] == NULL) { fprintf (stderr, "display-icon: no operating systems found\n"); exit (EXIT_FAILURE); } for (j = 0; roots[j] != NULL; ++j) { root = roots[j]; /* Mount up the disks, like guestfish -i. * * Sort keys by length, shortest first, so that we end up * mounting the filesystems in the correct order. */ mountpoints = guestfs_inspect_get_mountpoints (g, root); if (mountpoints == NULL) exit (EXIT_FAILURE); 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. */ guestfs_mount_ro (g, mountpoints[i+1], mountpoints[i]); free (mountpoints[i]); free (mountpoints[i+1]); } free (mountpoints); /* Get the icon. * This function returns a buffer ('icon'). Normally it is a png * file, returned as a string, but it can also be a zero length * buffer which has a special meaning, or NULL which means there * was an error. */ icon = guestfs_inspect_get_icon (g, root, &icon_size, -1); if (!icon) /* actual libguestfs error */ exit (EXIT_FAILURE); if (icon_size == 0) /* no icon available */ fprintf (stderr, "%s: %s: no icon available for this operating system\n", disk, root); else { /* Display the icon. */ fp = popen ("display -", "w"); if (fp == NULL) { perror ("display"); exit (EXIT_FAILURE); } if (fwrite (icon, 1, icon_size, fp) != icon_size) { perror ("write"); exit (EXIT_FAILURE); } if (pclose (fp) == -1) { perror ("pclose"); exit (EXIT_FAILURE); } } free (icon); /* Unmount everything. */ if (guestfs_umount_all (g) == -1) exit (EXIT_FAILURE); free (root); } free (roots); guestfs_close (g); exit (EXIT_SUCCESS); }
EXAMPLE: THE LIBVIRT AUTHENTICATION API¶
/* Example of using the libvirt authentication event-driven API. * * See "LIBVIRT AUTHENTICATION" in guestfs(3). */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <guestfs.h> static void usage (void) { fprintf (stderr, "Usage:\n" "\n" " libvirt-auth URI domain\n" "\n" "where:\n" "\n" " URI is the libvirt URI, eg. qemu+libssh2://USER@localhost/system\n" " domain is the name of the guest\n" "\n" "Example:\n" "\n" " libvirt-auth 'qemu+libssh2://USER@localhost/system' 'foo'\n" "\n" "would connect (read-only) to libvirt URI given and open the guest\n" "called 'foo' and list some information about its filesystems.\n" "\n" "The important point of this example is that any libvirt authentication\n" "required to connect to the server should be done.\n" "\n"); } static void auth_callback (guestfs_h *g, void *opaque, uint64_t event, int event_handle, int flags, const char *buf, size_t buf_len, const uint64_t *array, size_t array_len); int main (int argc, char *argv[]) { const char *uri, *dom; guestfs_h *g; const char *creds[] = { "authname", "passphrase", "echoprompt", "noechoprompt", NULL }; int r, eh; char **filesystems; size_t i; if (argc != 3) { usage (); exit (EXIT_FAILURE); } uri = argv[1]; dom = argv[2]; g = guestfs_create (); if (!g) exit (EXIT_FAILURE); r = guestfs_set_libvirt_supported_credentials (g, (char **) creds); if (r == -1) exit (EXIT_FAILURE); /* Set up the event handler. */ eh = guestfs_set_event_callback (g, auth_callback, GUESTFS_EVENT_LIBVIRT_AUTH, 0, NULL); if (eh == -1) exit (EXIT_FAILURE); /* Add the named domain. */ r = guestfs_add_domain (g, dom, GUESTFS_ADD_DOMAIN_LIBVIRTURI, uri, -1); if (r == -1) exit (EXIT_FAILURE); /* Launch and do some simple inspection. */ r = guestfs_launch (g); if (r == -1) exit (EXIT_FAILURE); filesystems = guestfs_list_filesystems (g); if (filesystems == NULL) exit (EXIT_FAILURE); for (i = 0; filesystems[i] != NULL; i += 2) { printf ("%s:%s is a %s filesystem\n", dom, filesystems[i], filesystems[i+1]); free (filesystems[i]); free (filesystems[i+1]); } free (filesystems); exit (EXIT_SUCCESS); } static void auth_callback (guestfs_h *g, void *opaque, uint64_t event, int event_handle, int flags, const char *buf, size_t buf_len, const uint64_t *array, size_t array_len) { char **creds; size_t i; char *prompt; char *reply = NULL; size_t allocsize = 0; char *pass; ssize_t len; int r; printf ("libvirt-auth.c: authentication required for libvirt URI '%s'\n\n", buf); /* Ask libguestfs what credentials libvirt is demanding. */ creds = guestfs_get_libvirt_requested_credentials (g); if (creds == NULL) exit (EXIT_FAILURE); /* Now ask the user for answers. */ for (i = 0; creds[i] != NULL; ++i) { printf ("libvirt-auth.c: credential '%s'\n", creds[i]); if (strcmp (creds[i], "authname") == 0 || strcmp (creds[i], "echoprompt") == 0) { prompt = guestfs_get_libvirt_requested_credential_prompt (g, i); if (prompt && strcmp (prompt, "") != 0) printf ("%s: ", prompt); free (prompt); len = getline (&reply, &allocsize, stdin); if (len == -1) { perror ("getline"); exit (EXIT_FAILURE); } if (len > 0 && reply[len-1] == '\n') reply[--len] = '\0'; r = guestfs_set_libvirt_requested_credential (g, i, reply, len); if (r == -1) exit (EXIT_FAILURE); } else if (strcmp (creds[i], "passphrase") == 0 || strcmp (creds[i], "noechoprompt") == 0) { prompt = guestfs_get_libvirt_requested_credential_prompt (g, i); if (prompt && strcmp (prompt, "") != 0) printf ("%s: ", prompt); free (prompt); pass = getpass (""); if (pass == NULL) { perror ("getpass"); exit (EXIT_FAILURE); } len = strlen (pass); r = guestfs_set_libvirt_requested_credential (g, i, pass, len); if (r == -1) exit (EXIT_FAILURE); } free (creds[i]); } free (reply); free (creds); }
EXAMPLE: THE MOUNT LOCAL API¶
/* Demonstrate the use of the 'mount-local' API. * * Run this program as (eg) mount-local /tmp/test.img. Note that * '/tmp/test.img' is created or overwritten. Follow the instructions * on screen. * * See "MOUNT LOCAL" in guestfs(3). */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/wait.h> #include <guestfs.h> #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif /* Define a list of filesystem mount options (used on the libguestfs * side, nothing to do with FUSE). An empty string may be used here * instead. */ #define MOUNT_OPTIONS "acl,user_xattr" /* Size of the disk (megabytes). */ #define SIZE_MB 512 static void usage (void) { fprintf (stderr, "Usage: mount-local disk.img\n" "\n" "NOTE: disk.img will be created or overwritten.\n" "\n"); } int main (int argc, char *argv[]) { guestfs_h *g; int r; char tempdir[] = "/tmp/mlXXXXXX"; pid_t pid; char *shell, *p; if (argc != 2) { usage (); exit (EXIT_FAILURE); } if (argv[1][0] == '-') { usage (); exit (EXIT_FAILURE); } printf ("\n" "This is the 'mount-local' demonstration program. Follow the\n" "instructions on screen.\n" "\n" "Creating and formatting the disk image, please wait a moment ...\n"); fflush (stdout); /* Guestfs handle. */ g = guestfs_create (); if (g == NULL) { perror ("could not create libguestfs handle"); exit (EXIT_FAILURE); } /* Create the output disk image: raw sparse. */ if (guestfs_disk_create (g, argv[1], "raw", SIZE_MB * 1024 * 1024, -1) == -1) exit (EXIT_FAILURE); /* Create the disk image and format it with a partition and a filesystem. */ if (guestfs_add_drive_opts (g, argv[1], GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw", -1) == -1) exit (EXIT_FAILURE); if (guestfs_launch (g) == -1) exit (EXIT_FAILURE); if (guestfs_part_disk (g, "/dev/sda", "mbr") == -1) exit (EXIT_FAILURE); if (guestfs_mkfs (g, "ext2", "/dev/sda1") == -1) exit (EXIT_FAILURE); /* Mount the empty filesystem. */ if (guestfs_mount_options (g, MOUNT_OPTIONS, "/dev/sda1", "/") == -1) exit (EXIT_FAILURE); /* Create a file in the new filesystem. */ if (guestfs_touch (g, "/PUT_FILES_AND_DIRECTORIES_HERE") == -1) exit (EXIT_FAILURE); /* Create a temporary mount directory. */ if (mkdtemp (tempdir) == NULL) { perror ("mkdtemp"); exit (EXIT_FAILURE); } /* Mount the filesystem. */ if (guestfs_mount_local (g, tempdir, -1) == -1) exit (EXIT_FAILURE); /* Fork the shell for the user. */ pid = fork (); if (pid == -1) { perror ("fork"); exit (EXIT_FAILURE); } if (pid == 0) { /* Child. */ if (chdir (tempdir) == -1) { perror (tempdir); _exit (EXIT_FAILURE); } printf ("\n" "The *current directory* is a FUSE filesystem backed by the disk\n" "image which is managed by libguestfs. Any files or directories\n" "you copy into here (up to %d MB) will be saved into the disk\n" "image. You can also delete files, create certain special files\n" "and so on.\n" "\n" "When you have finished adding files, hit ^D or type 'exit' to\n" "exit the shell and return to the mount-local program.\n" "\n", SIZE_MB); shell = getenv ("SHELL"); if (!shell) r = system ("/bin/sh"); else { /* Set a magic prompt. We only know how to do this for bash. */ p = strrchr (shell, '/'); if (p && strcmp (p+1, "bash") == 0) { const size_t len = 64 + strlen (shell); char *buf; buf = malloc (len); if (buf == NULL) { perror ("malloc"); _exit (EXIT_FAILURE); } snprintf (buf, len, "PS1='mount-local-shell> ' %s --norc -i", shell); r = system (buf); free (buf); } else r = system (shell); } if (r == -1) { fprintf (stderr, "error: failed to run sub-shell (%s) " "(is $SHELL set correctly?)\n", shell); //FALLTHROUGH } if (chdir ("/") == -1) perror ("chdir: /"); guestfs_umount_local (g, GUESTFS_UMOUNT_LOCAL_RETRY, 1, -1); _exit (EXIT_SUCCESS); } /* Note that we are *not* waiting for the child yet. We want to * run the FUSE code in parallel with the subshell. */ /* We're going to hide libguestfs errors here, but in a real program * you would probably want to log them somewhere. */ guestfs_push_error_handler (g, NULL, NULL); /* Now run the FUSE thread. */ if (guestfs_mount_local_run (g) == -1) exit (EXIT_FAILURE); guestfs_pop_error_handler (g); waitpid (pid, NULL, 0); /* Shutdown the handle explicitly so write errors can be detected. */ if (guestfs_shutdown (g) == -1) exit (EXIT_FAILURE); guestfs_close (g); printf ("\n" "Any files or directories that you copied in have been saved into\n" "the disk image called '%s'.\n" "\n" "Try opening the disk image with guestfish to see those files:\n" "\n" " guestfish -a %s -m /dev/sda1\n" "\n", argv[1], argv[1]); exit (EXIT_SUCCESS); }
EXAMPLE: MULTIPLE HANDLES AND THREADS¶
/* Copy a directory from one libvirt guest to another. * * This is a more substantial example of using the libguestfs API, * demonstrating amongst other things: * * - using multiple handles with threads * - upload and downloading (using a pipe between handles) * - inspection */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <inttypes.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <sys/time.h> #include <pthread.h> #include <guestfs.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 int64_t timeval_diff (const struct timeval *x, const struct timeval *y); static int compare_keys_len (const void *p1, const void *p2); static size_t count_strings (char *const *argv); 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/dir\n" "would copy /home/rjones from Src to /tmp/dir 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]; struct timeval start_t, end_t; int64_t ms; 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 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); } gettimeofday (&start_t, NULL); /* 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_shutdown (destg) == -1) exit (EXIT_FAILURE); guestfs_close (destg); gettimeofday (&end_t, NULL); /* Print the elapsed time. */ ms = timeval_diff (&start_t, &end_t); printf ("copy finished, elapsed time (excluding launch) was " "%" PRIi64 ".%03" PRIi64 " s\n", ms / 1000, ms % 1000); 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. */ guestfs_close (srcg); return NULL; } /* 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; } /* Compute Y - X and return the result in milliseconds. * Approximately the same as this code: * http://www.mpp.mpg.de/~huber/util/timevaldiff.c */ static int64_t timeval_diff (const struct timeval *x, const struct timeval *y) { int64_t msec; msec = (y->tv_sec - x->tv_sec) * 1000; msec += (y->tv_usec - x->tv_usec) / 1000; return msec; } 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; }
EXAMPLE: FETCH DHCP ADDRESS FROM A GUEST¶
/* This is a more significant example of a tool which can grab the * DHCP address from some types of virtual machine. Since there are * so many possible ways to do this, without clarity on which is the * best way, I don't want to make this into an official virt tool. * * For more information, see: * * https://rwmj.wordpress.com/2010/10/26/tip-find-the-ip-address-of-a-virtual-machine/ * https://rwmj.wordpress.com/2011/03/30/tip-another-way-to-get-the-ip-address-of-a-virtual-machine/ */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <assert.h> #include <guestfs.h> static int compare_keys_len (const void *p1, const void *p2); static size_t count_strings (char *const *argv); static void free_strings (char **argv); static void mount_disks (guestfs_h *g, char *root); static void print_dhcp_address (guestfs_h *g, char *root); static void print_dhcp_address_linux (guestfs_h *g, char *root, const char *logfile); static void print_dhcp_address_windows (guestfs_h *g, char *root); int main (int argc, char *argv[]) { guestfs_h *g; size_t i; char **roots, *root; if (argc < 2) { fprintf (stderr, "Usage: virt-dhcp-address disk.img [disk.img [...]]\n" "Note that all disks must come from a single virtual machine.\n"); exit (EXIT_FAILURE); } g = guestfs_create (); if (g == NULL) { perror ("failed to create libguestfs handle"); exit (EXIT_FAILURE); } for (i = 1; i < (size_t) argc; ++i) { /* Attach the disk image(s) read-only to libguestfs. */ if (guestfs_add_drive_opts (g, argv[i], /* GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw", */ GUESTFS_ADD_DRIVE_OPTS_READONLY, 1, -1) /* this marks end of optional arguments */ == -1) exit (EXIT_FAILURE); } /* Run the libguestfs back-end. */ if (guestfs_launch (g) == -1) exit (EXIT_FAILURE); /* Ask libguestfs to inspect for operating systems. */ roots = guestfs_inspect_os (g); if (roots == NULL) exit (EXIT_FAILURE); if (roots[0] == NULL) { fprintf (stderr, "virt-dhcp-address: no operating systems found\n"); exit (EXIT_FAILURE); } if (count_strings (roots) > 1) { fprintf (stderr, "virt-dhcp-address: multi-boot operating system\n"); exit (EXIT_FAILURE); } root = roots[0]; /* Mount up the guest's disks. */ mount_disks (g, root); /* Print DHCP address. */ print_dhcp_address (g, root); /* Close handle and exit. */ guestfs_close (g); free_strings (roots); exit (EXIT_SUCCESS); } static void mount_disks (guestfs_h *g, char *root) { char **mountpoints; size_t i; /* Mount up the disks, like guestfish -i. * * Sort keys by length, shortest first, so that we end up * mounting the filesystems in the correct order. */ mountpoints = guestfs_inspect_get_mountpoints (g, root); if (mountpoints == NULL) exit (EXIT_FAILURE); 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. */ guestfs_mount_ro (g, mountpoints[i+1], mountpoints[i]); } free_strings (mountpoints); } static void print_dhcp_address (guestfs_h *g, char *root) { char *guest_type, *guest_distro; /* Depending on the guest type, try to get the DHCP address. */ guest_type = guestfs_inspect_get_type (g, root); if (guest_type == NULL) exit (EXIT_FAILURE); if (strcmp (guest_type, "linux") == 0) { guest_distro = guestfs_inspect_get_distro (g, root); if (guest_distro == NULL) exit (EXIT_FAILURE); if (strcmp (guest_distro, "fedora") == 0 || strcmp (guest_distro, "rhel") == 0 || strcmp (guest_distro, "redhat-based") == 0) { print_dhcp_address_linux (g, root, "/var/log/messages"); } else if (strcmp (guest_distro, "debian") == 0 || strcmp (guest_distro, "ubuntu") == 0) { print_dhcp_address_linux (g, root, "/var/log/syslog"); } else { fprintf (stderr, "virt-dhcp-address: don't know how to get DHCP address from '%s'\n", guest_distro); exit (EXIT_FAILURE); } free (guest_distro); } else if (strcmp (guest_type, "windows") == 0) { print_dhcp_address_windows (g, root); } else { fprintf (stderr, "virt-dhcp-address: don't know how to get DHCP address from '%s'\n", guest_type); exit (EXIT_FAILURE); } free (guest_type); } /* Look for dhclient messages in logfile. */ static void print_dhcp_address_linux (guestfs_h *g, char *root, const char *logfile) { char **lines, *p; size_t len; lines = guestfs_grep_opts (g, "dhclient.*: bound to ", logfile, GUESTFS_GREP_OPTS_EXTENDED, 1, -1); if (lines == NULL) exit (EXIT_FAILURE); len = count_strings (lines); if (len == 0) { fprintf (stderr, "virt-dhcp-address: cannot find DHCP address for this guest.\n"); exit (EXIT_FAILURE); } /* Only want the last message. */ p = strstr (lines[len-1], "bound to "); assert (p); p += 9; len = strcspn (p, " "); p[len] = '\0'; printf ("%s\n", p); free_strings (lines); } /* Download the Windows SYSTEM hive and find DHCP configuration in there. */ static void print_dhcp_address_windows (guestfs_h *g, char *root_fs) { char *system_path; int64_t root, node, value; struct guestfs_hivex_node_list *nodes; char *controlset; size_t i; char *p; /* Locate the SYSTEM hive. */ system_path = guestfs_inspect_get_windows_system_hive (g, root_fs); if (!system_path) exit (EXIT_FAILURE); /* Open the hive to parse it. Note that before libguestfs 1.19.35 * you had to download the file and parse it using hivex(3). Since * libguestfs 1.19.35, parts of the hivex(3) API are now exposed * through libguestfs, and that is what we'll use here because it is * more convenient and avoids having to download the hive. */ if (guestfs_hivex_open (g, system_path, -1) == -1) exit (EXIT_FAILURE); free (system_path); root = guestfs_hivex_root (g); if (root == -1) exit (EXIT_FAILURE); /* Get ControlSetXXX\Services\Tcpip\Parameters\Interfaces. */ controlset = guestfs_inspect_get_windows_current_control_set (g, root_fs); if (controlset == NULL) exit (EXIT_FAILURE); const char *path[] = { controlset, "Services", "Tcpip", "Parameters", "Interfaces" }; node = root; for (i = 0; node > 0 && i < sizeof path / sizeof path[0]; ++i) node = guestfs_hivex_node_get_child (g, node, path[i]); if (node == -1) exit (EXIT_FAILURE); if (node == 0) { fprintf (stderr, "virt-dhcp-address: HKLM\\System\\%s\\Services\\Tcpip\\Parameters\\Interfaces not found.", controlset); exit (EXIT_FAILURE); } free (controlset); /* Look for a node under here which has a "DhcpIPAddress" entry in it. */ nodes = guestfs_hivex_node_children (g, node); if (nodes == NULL) exit (EXIT_FAILURE); value = 0; for (i = 0; value == 0 && i < nodes->len; ++i) { value = guestfs_hivex_node_get_value (g, nodes->val[i].hivex_node_h, "DhcpIPAddress"); if (value == -1) exit (EXIT_FAILURE); } if (value == 0) { fprintf (stderr, "virt-dhcp-address: cannot find DHCP address for this guest.\n"); exit (EXIT_FAILURE); } guestfs_free_hivex_node_list (nodes); /* Get the string and use libguestfs's auto-conversion to convert it * to UTF-8 for output. */ p = guestfs_hivex_value_string (g, value); if (!p) exit (EXIT_FAILURE); printf ("%s\n", p); free (p); /* Close the hive handle. */ guestfs_hivex_close (g); } 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; } static void free_strings (char **argv) { size_t i; for (i = 0; argv[i]; ++i) free (argv[i]); free (argv); }
SEE ALSO¶
guestfs(3), guestfs-erlang(3), guestfs-golang(3), guestfs-java(3), guestfs-lua(3), guestfs-ocaml(3), guestfs-perl(3), guestfs-python(3), guestfs-recipes(1), guestfs-ruby(3), http://libguestfs.org/.
AUTHORS¶
Richard W.M. Jones ("rjones at redhat dot com")
COPYRIGHT¶
Copyright (C) 2010-2023 Red Hat Inc.
LICENSE¶
This manual page contains examples which we hope you will use in your programs. The examples may be freely copied, modified and distributed for any purpose without any restrictions.
BUGS¶
To get a list of bugs against libguestfs, use this link: https://bugzilla.redhat.com/buglist.cgi?component=libguestfs&product=Virtualization+Tools
To report a new bug against libguestfs, use this link: https://bugzilla.redhat.com/enter_bug.cgi?component=libguestfs&product=Virtualization+Tools
When reporting a bug, please supply:
- The version of libguestfs.
- Where you got libguestfs (eg. which Linux distro, compiled from source, etc)
- Describe the bug accurately and give a way to reproduce it.
- Run libguestfs-test-tool(1) and paste the complete, unedited output into the bug report.
2024-10-08 | libguestfs-1.54.0 |