Files
libguestfs/examples/to-xml.c
Richard W.M. Jones 14490c3e1a generator: Optional arguments, add-drive-opts (RHBZ#642934,CVE-2010-3851).
This large commit changes the generator so that optional arguments
can be supported for functions.

The model for arguments (known as the "style") is changed from
(ret, args) to (ret, args, optargs) where optargs is a more limited
list of arguments.

One function has been added which takes optional arguments, it is
"add-drive-opts", modelled as:

  (RErr, [String "filename"], #required
         [Bool "readonly"; String "format"; String "iface"]) #optional

Note that this function is processed in the library (does not go over
the RPC protocol to the daemon).  This has allowed us to simplify
the current implementation by omitting changes related to RPC or the
daemon, although we plan to add these at some point in the future.

From C this function can be called in 3 different ways as in these
examples:

  guestfs_add_drive_opts (g, filename,
                          GUESTFS_ADD_DRIVE_OPTS_READONLY, 1,
			  GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw",
                          -1);

(the argument(s) between 'filename' and '-1' are the optional ones).

  guestfs_add_drive_opts_va (g, filename, args);

where 'args' is a va_list.  This works like the first version.

  struct guestfs_add_drive_opts_argv optargs = {
    .bitmask = GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK,
    .readonly = 1,
  }
  guestfs_add_drive_opts_argv (g, filename, &optargs);

This last form lets you construct lists of optional arguments, and
is used by guestfish and the language bindings.

In guestfish optional arguments are used like this:

  add-drive-opts filename readonly:true

In OCaml these are mapped naturally to OCaml optional arguments, eg:

  g#add_drive_opts ~readonly:true filename;

In Perl these are mapped to extra arguments, eg:

  $g->add_drive_opts ($filename, readonly => 1);

In Python these are mapped to optional arguments, eg:

  g.add_drive_opts ("file", readonly = 1, format = "qcow2")

In Ruby these are mapped to a final hash argument, eg:

  g.add_drive_opts("file", {})
  g.add_drive_opts("file", :readonly => 1)
  g.add_drive_opts("file", :readonly => 1, :iface => "virtio")

In PHP these are mapped to extra parameters.  This is not quite
accurate since you cannot omit arbitrary optional parameters, but
there's not much than can be done within the limitations of PHP
as a language.

Unimplemented in: Haskell, C#, Java.
2010-10-22 17:45:00 +01:00

207 lines
5.8 KiB
C

/* 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 <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <unistd.h>
#include <ctype.h>
#include <guestfs.h>
/* 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 ("<guestfs-system>\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 ("<devices>\n");
for (i = 0; devices[i] != NULL; ++i) {
int64_t size;
CALL (size = guestfs_blockdev_getsize64 (g, devices[i]), -1);
printf ("<device dev=\"%s\" size=\"%" PRIi64 "\">\n", devices[i], size);
display_partitions (g, devices[i]);
free (devices[i]);
printf ("</device>\n");
}
free (devices);
printf ("</devices>\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 ("<volgroups>\n");
CALL (vgs = guestfs_vgs (g), NULL);
CALL (lvs = guestfs_lvs (g), NULL);
for (i = 0; vgs[i] != NULL; ++i) {
printf ("<volgroup name=\"%s\">\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 ("<logvol name=\"%s\" size=\"%" PRIi64 "\">\n", lvs[j], size);
display_partition (g, lvs[j]);
printf ("</logvol>\n");
free (lvs[j]);
}
}
free (vgs[i]);
printf ("</volgroup>\n");
}
free (vgs);
free (lvs);
printf ("</volgroups>\n");
guestfs_close (g);
printf ("</guestfs-system>\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 ("<windows/>\n");
else if (strstr (what, "boot sector") != NULL)
display_partitions (g, dev);
else if (strncmp (what, "LVM2", 4) == 0)
printf ("<physvol/>\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 ("<linux-swap/>\n");
else
printf ("<unknown/>\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 ("<vm-image dev=\"%s\"/>\n", dev);
return;
}
char **parts;
int i, len;
CALL (parts = guestfs_list_partitions (g), NULL);
printf ("<partitions>\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 ("<partition dev=\"%s\" size=\"%" PRIi64 "\">\n", parts[i], size);
display_partition (g, parts[i]);
printf ("</partition>\n");
}
free (parts[i]);
}
free (parts);
printf ("</partitions>\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 ("<fs type=\"%s\">\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 ("<uuid>%s</uuid>\n", sbfields[i+1]);
else if (strcmp (sbfields[i], "Block size") == 0)
printf ("<blocksize>%s</blocksize>\n", sbfields[i+1]);
free (sbfields[i]);
free (sbfields[i+1]);
}
free (sbfields);
printf ("</fs>\n");
}