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. |