From 58012dc9b67aa1b5de9c5dc70e7e471de3ed2a6a Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Wed, 24 Nov 2010 17:19:28 +0000 Subject: [PATCH] docs: Standard C examples, and guestfs-examples(3) man page. --- .gitignore | 7 +- Makefile.am | 1 + examples/LICENSE | 4 +- examples/Makefile.am | 54 ++++++++--- examples/README | 15 --- examples/create_disk.c | 122 +++++++++++++++++++++++++ examples/guestfs-examples.pod | 63 +++++++++++++ examples/hello.c | 41 --------- examples/inspect_vm.c | 132 +++++++++++++++++++++++++++ examples/to-xml.c | 206 ------------------------------------------ src/guestfs.pod | 4 + 11 files changed, 371 insertions(+), 278 deletions(-) delete mode 100644 examples/README create mode 100644 examples/create_disk.c create mode 100644 examples/guestfs-examples.pod delete mode 100644 examples/hello.c create mode 100644 examples/inspect_vm.c delete mode 100644 examples/to-xml.c diff --git a/.gitignore b/.gitignore index ac7ebbd..86dc196 100644 --- a/.gitignore +++ b/.gitignore @@ -76,8 +76,10 @@ daemon/stubs.c depcomp .deps emptydisk -examples/hello -examples/to-xml +examples/create_disk +examples/guestfs-examples.3 +examples/inspect_vm +examples/stamp-guestfs-examples.pod fish/cmds.c fish/cmds_gperf.c fish/cmds_gperf.gperf @@ -113,6 +115,7 @@ haskell/Guestfs.hs *.hi html/guestfish.1.html html/guestfs.3.html +html/guestfs-examples.3.html html/guestmount.1.html html/recipes.html html/virt-cat.1.html diff --git a/Makefile.am b/Makefile.am index 94a3017..b154f08 100644 --- a/Makefile.am +++ b/Makefile.am @@ -102,6 +102,7 @@ html/recipes.html: $(wildcard recipes/*.sh) $(wildcard recipes/*.html) $(wildcar HTMLFILES = \ html/guestfs.3.html \ + html/guestfs-examples.3.html \ html/guestfish.1.html \ html/guestmount.1.html \ html/virt-cat.1.html \ diff --git a/examples/LICENSE b/examples/LICENSE index 5ba695a..ce7b417 100644 --- a/examples/LICENSE +++ b/examples/LICENSE @@ -1,2 +1,2 @@ -All the examples in the examples/ subdirectory may be freely copied -without any restrictions. +All the examples in the 'examples' subdirectory may be freely copied, +modified and distributed without any restrictions. diff --git a/examples/Makefile.am b/examples/Makefile.am index 04bbe5e..7f7d8f7 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -1,17 +1,47 @@ -# libguestfs examples +# libguestfs C examples +# Copyright (C) 2010 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., 675 Mass Ave, Cambridge, MA 02139, USA. -include $(top_srcdir)/subdir-rules.mk +EXTRA_DIST = \ + LICENSE \ + guestfs-examples.pod -noinst_PROGRAMS = hello to-xml +CLEANFILES = stamp-guestfs-examples.pod -hello_SOURCES = hello.c -hello_CFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src -Wall -hello_LDADD = $(top_builddir)/src/libguestfs.la +noinst_PROGRAMS = create_disk inspect_vm -to_xml_SOURCES = to-xml.c -to_xml_CPPFLAGS = \ - -I$(top_srcdir)/gnulib/lib \ - -I$(top_srcdir)/src -I$(top_builddir)/src -Wall -to_xml_LDADD = $(top_builddir)/src/libguestfs.la +create_disk_SOURCES = create_disk.c +create_disk_CFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src -Wall +create_disk_LDADD = $(top_builddir)/src/libguestfs.la -CLEANFILES = $(noinst_PROGRAMS) +inspect_vm_SOURCES = inspect_vm.c +inspect_vm_CFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src -Wall +inspect_vm_LDADD = $(top_builddir)/src/libguestfs.la + +man_MANS = guestfs-examples.3 +noinst_DATA = $(top_builddir)/html/guestfs-examples.3.html + +guestfs-examples.3 $(top_builddir)/html/guestfs-examples.3.html: stamp-guestfs-examples.pod + +stamp-guestfs-examples.pod: guestfs-examples.pod create_disk.c inspect_vm.c + $(top_srcdir)/podwrapper.sh \ + --section 3 \ + --man guestfs-examples.3 \ + --html $(top_builddir)/html/guestfs-examples.3.html \ + --verbatim create_disk.c:@EXAMPLE1@ \ + --verbatim inspect_vm.c:@EXAMPLE2@ \ + $< + touch $@ diff --git a/examples/README b/examples/README deleted file mode 100644 index 408d6a2..0000000 --- a/examples/README +++ /dev/null @@ -1,15 +0,0 @@ -This directory contains various example programs which use the -libguestfs API. - -As they are examples, these are licensed so they can be freely copied -and used without any restrictions. - -Tips: - -(1) To enable verbose messages, set environment variable -LIBGUESTFS_DEBUG=1 - -(2) If you haven't installed libguestfs, run the examples like this: -LIBGUESTFS_PATH=appliance examples/to-xml -(the path should point to the directory containing vmlinuz.* and -initramfs.* files). diff --git a/examples/create_disk.c b/examples/create_disk.c new file mode 100644 index 0000000..f4b9044 --- /dev/null +++ b/examples/create_disk.c @@ -0,0 +1,122 @@ +/* Example showing how to create a disk image. */ + +#include +#include +#include +#include +#include +#include + +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); + } + + /* Create a raw-format sparse disk image, 512 MB in size. */ + int fd = open ("disk.img", O_CREAT|O_WRONLY|O_TRUNC|O_NOCTTY, 0666); + if (fd == -1) { + perror ("disk.img"); + exit (EXIT_FAILURE); + } + if (ftruncate (fd, 512 * 1024 * 1024) == -1) { + perror ("disk.img: truncate"); + exit (EXIT_FAILURE); + } + if (close (fd) == -1) { + perror ("disk.img: close"); + exit (EXIT_FAILURE); + } + + /* Set the trace flag so that we can see each libguestfs call. */ + guestfs_set_trace (g, 1); + + /* Set the autosync flag so that the disk will be synchronized + * automatically when the libguestfs handle is closed. + */ + guestfs_set_autosync (g, 1); + + /* 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_options (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 'autosync' was set (above) we can just close the handle + * and the disk contents will be synchronized. You can also do + * this manually by calling guestfs_umount_all and guestfs_sync. + */ + 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); +} diff --git a/examples/guestfs-examples.pod b/examples/guestfs-examples.pod new file mode 100644 index 0000000..858ac61 --- /dev/null +++ b/examples/guestfs-examples.pod @@ -0,0 +1,63 @@ +=encoding utf8 + +=head1 NAME + +guestfs-examples - Examples of using libguestfs from C + +=head1 SYNOPSIS + + #include + + 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` + +=head1 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 L. + +=head1 EXAMPLE 1: CREATE A DISK IMAGE + +@EXAMPLE1@ + +=head1 EXAMPLE 2: INSPECT A VIRTUAL MACHINE DISK IMAGE + +@EXAMPLE2@ + +=head1 SEE ALSO + +L, +L, +L, +L. + +=head1 AUTHORS + +Richard W.M. Jones (C) + +=head1 COPYRIGHT + +Copyright (C) 2010 Red Hat Inc. L + +The examples in this manual page may be freely copied, modified and +distributed without any restrictions. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/examples/hello.c b/examples/hello.c deleted file mode 100644 index b3d36e6..0000000 --- a/examples/hello.c +++ /dev/null @@ -1,41 +0,0 @@ -/* Create a "/hello" file on chosen partition. - * eg: - * hello guest.img /dev/sda1 - * hello guest.img /dev/VolGroup00/LogVol00 - */ - -#if HAVE_CONFIG_H -# include -#endif -#include -#include -#include -#include - -int -main (int argc, char *argv[]) -{ - guestfs_h *g; - - if (argc != 3 || access (argv[1], F_OK) != 0) { - fprintf (stderr, "Usage: hello disk-image partition\n"); - exit (EXIT_FAILURE); - } - - if (!(g = guestfs_create ())) exit (EXIT_FAILURE); - - 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_mount_options (g, "", argv[2], "/") == -1) exit (EXIT_FAILURE); - - if (guestfs_touch (g, "/hello") == -1) exit (EXIT_FAILURE); - - guestfs_sync (g); - guestfs_close (g); - return 0; -} diff --git a/examples/inspect_vm.c b/examples/inspect_vm.c new file mode 100644 index 0000000..4019a01 --- /dev/null +++ b/examples/inspect_vm.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include + +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 int +count_strings (char *const *argv) +{ + int 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) { + if (guestfs_mount_ro (g, mountpoints[i+1], mountpoints[i]) == -1) + exit (EXIT_FAILURE); + 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); +} diff --git a/examples/to-xml.c b/examples/to-xml.c deleted file mode 100644 index 45994cb..0000000 --- a/examples/to-xml.c +++ /dev/null @@ -1,206 +0,0 @@ -/* This inspects a block device and produces an XML representation of - * the partitions, LVM, filesystems that we find there. This could be - * useful as example code of how to do this sort of probing, or to - * feed the XML to other programs. - * - * Usage: - * to-xml guest.img [guest.img ...] - */ - -#if HAVE_CONFIG_H -# include -#endif -#include -#include -#include -#include -#include -#include -#include - -#include - -/* Note that if any API call fails, we can just exit. The - * standard error handler will have printed the error message - * to stderr already. - */ -#define CALL(call,errcode) \ - if ((call) == (errcode)) exit (EXIT_FAILURE); - -static void display_partition (guestfs_h *g, const char *dev); -static void display_partitions (guestfs_h *g, const char *dev); -static void display_ext234 (guestfs_h *g, const char *dev, const char *fstype); - -int -main (int argc, char *argv[]) -{ - guestfs_h *g; - int i; - - if (argc < 2 || access (argv[1], F_OK) != 0) { - fprintf (stderr, "Usage: to-xml guest.img [guest.img ...]\n"); - exit (EXIT_FAILURE); - } - - if (!(g = guestfs_create ())) { - fprintf (stderr, "Cannot create libguestfs handle.\n"); - exit (EXIT_FAILURE); - } - - for (i = 1; i < argc; ++i) - CALL (guestfs_add_drive_opts (g, argv[i], - GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw", - -1), -1); - - CALL (guestfs_launch (g), -1); - - printf ("\n"); - - /* list-devices should return the devices that we just attached? - * Better to find out what the kernel thinks are devices anyway ... - */ - char **devices; - CALL (devices = guestfs_list_devices (g), NULL); - printf ("\n"); - for (i = 0; devices[i] != NULL; ++i) { - int64_t size; - CALL (size = guestfs_blockdev_getsize64 (g, devices[i]), -1); - printf ("\n", devices[i], size); - display_partitions (g, devices[i]); - free (devices[i]); - printf ("\n"); - } - free (devices); - printf ("\n"); - - /* Now do the same for VGs and LVs. Note that a VG may span - * multiple PVs / block devices, in arbitrary ways, which is - * why VGs are in a separate top-level XML class. - */ - char **vgs; - char **lvs; - printf ("\n"); - CALL (vgs = guestfs_vgs (g), NULL); - CALL (lvs = guestfs_lvs (g), NULL); - for (i = 0; vgs[i] != NULL; ++i) { - printf ("\n", vgs[i]); - - /* Just the LVs in this VG. */ - int len = strlen (vgs[i]); - int j; - for (j = 0; lvs[j] != NULL; ++j) { - if (strncmp (lvs[j], "/dev/", 5) == 0 && - strncmp (&lvs[j][5], vgs[i], len) == 0 && - lvs[j][len+5] == '/') { - int64_t size; - CALL (size = guestfs_blockdev_getsize64 (g, lvs[j]), -1); - printf ("\n", lvs[j], size); - display_partition (g, lvs[j]); - printf ("\n"); - free (lvs[j]); - } - } - - free (vgs[i]); - printf ("\n"); - } - free (vgs); - free (lvs); - printf ("\n"); - - guestfs_close (g); - printf ("\n"); - - return 0; -} - -/* Display a partition or LV. */ -static void -display_partition (guestfs_h *g, const char *dev) -{ - char *what; - - CALL (what = guestfs_file (g, dev), NULL); - - if (strcmp (what, "x86 boot sector") == 0) - /* This is what 'file' program shows for Windows/NTFS partitions. */ - printf ("\n"); - else if (strstr (what, "boot sector") != NULL) - display_partitions (g, dev); - else if (strncmp (what, "LVM2", 4) == 0) - printf ("\n"); - else if (strstr (what, "ext2 filesystem data") != NULL) - display_ext234 (g, dev, "ext2"); - else if (strstr (what, "ext3 filesystem data") != NULL) - display_ext234 (g, dev, "ext3"); - else if (strstr (what, "ext4 filesystem data") != NULL) - display_ext234 (g, dev, "ext4"); - else if (strstr (what, "Linux/i386 swap file") != NULL) - printf ("\n"); - else - printf ("\n"); - - free (what); -} - -/* Display an MBR-formatted boot sector. */ -static void -display_partitions (guestfs_h *g, const char *dev) -{ - /* We can't look into a boot sector which is an LV or partition. - * That's a limitation of sorts of the Linux kernel. (Actually, - * we could do this if we add the kpartx program to libguestfs). - */ - if (strncmp (dev, "/dev/sd", 7) != 0 || isdigit (dev[strlen(dev)-1])) { - printf ("\n", dev); - return; - } - - char **parts; - int i, len; - CALL (parts = guestfs_list_partitions (g), NULL); - printf ("\n"); - - len = strlen (dev); - for (i = 0; parts[i] != NULL; ++i) { - /* Only display partition if it's in the device. */ - if (strncmp (parts[i], dev, len) == 0) { - int64_t size; - CALL (size = guestfs_blockdev_getsize64 (g, parts[i]), -1); - printf ("\n", parts[i], size); - display_partition (g, parts[i]); - printf ("\n"); - } - - free (parts[i]); - } - free (parts); - printf ("\n"); -} - -/* Display some details on the ext2/3/4 filesystem on dev. */ -static void -display_ext234 (guestfs_h *g, const char *dev, const char *fstype) -{ - char **sbfields; - int i; - - printf ("\n", fstype); - CALL (sbfields = guestfs_tune2fs_l (g, dev), NULL); - - for (i = 0; sbfields[i] != NULL; i += 2) { - /* Just pick out a few important fields to display. There - * is much more that could be displayed here. - */ - if (strcmp (sbfields[i], "Filesystem UUID") == 0) - printf ("%s\n", sbfields[i+1]); - else if (strcmp (sbfields[i], "Block size") == 0) - printf ("%s\n", sbfields[i+1]); - - free (sbfields[i]); - free (sbfields[i+1]); - } - free (sbfields); - - printf ("\n"); -} diff --git a/src/guestfs.pod b/src/guestfs.pod index 7cf4e0b..966fbd1 100644 --- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -52,6 +52,9 @@ need enough permissions to access the disk images. Libguestfs is a large API because it can do many things. For a gentle introduction, please read the L section next. +There are also some example programs in the L +manual page. + =head1 API OVERVIEW This section provides a gentler overview of the libguestfs API. We @@ -2083,6 +2086,7 @@ enough. =head1 SEE ALSO +L, L, L, L, -- 1.8.3.1