From: Richard W.M. Jones Date: Thu, 31 Mar 2011 19:20:04 +0000 (+0100) Subject: examples: Add virt-dhcp-address program. X-Git-Tag: 1.9.15~3 X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=commitdiff_plain;h=97d737a88b33d3df97ceacb026b29b1656604a8d examples: Add virt-dhcp-address program. This is like the mythical 'virt-ifconfig'. There is not enough certainty around the right way to be doing this for us to make a full virt tool for this. Therefore the code is just an example. --- diff --git a/.gitignore b/.gitignore index 1f8ead3..9a844fa 100644 --- a/.gitignore +++ b/.gitignore @@ -73,6 +73,7 @@ examples/create_disk examples/guestfs-examples.3 examples/inspect_vm examples/stamp-guestfs-examples.pod +examples/virt-dhcp-address fish/cmds.c fish/cmds_gperf.c fish/cmds_gperf.gperf diff --git a/configure.ac b/configure.ac index 7736bd7..1acb5ba 100644 --- a/configure.ac +++ b/configure.ac @@ -449,6 +449,7 @@ PKG_CHECK_MODULES([HIVEX], [hivex], AC_DEFINE([HAVE_HIVEX],[1],[hivex library found at compile time.]) ], [AC_MSG_WARN([hivex not found, some core features will be disabled])]) +AM_CONDITIONAL([HAVE_HIVEX],[test "x$HIVEX_LIBS" != "x"]) dnl FUSE is optional to build the FUSE module. AC_ARG_ENABLE([fuse], diff --git a/examples/Makefile.am b/examples/Makefile.am index dee0849..00a5267 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -22,6 +22,9 @@ EXTRA_DIST = \ CLEANFILES = stamp-guestfs-examples.pod noinst_PROGRAMS = create_disk inspect_vm +if HAVE_HIVEX +noinst_PROGRAMS += virt-dhcp-address +endif create_disk_SOURCES = create_disk.c create_disk_CFLAGS = \ @@ -37,6 +40,15 @@ inspect_vm_CFLAGS = \ inspect_vm_LDADD = \ $(top_builddir)/src/libguestfs.la +virt_dhcp_address_SOURCES = virt-dhcp-address.c +virt_dhcp_address_CFLAGS = \ + -I$(top_srcdir)/src -I$(top_builddir)/src \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ + $(HIVEX_CFLAGS) +virt_dhcp_address_LDADD = \ + $(HIVEX_LIBS) \ + $(top_builddir)/src/libguestfs.la + man_MANS = guestfs-examples.3 noinst_DATA = $(top_builddir)/html/guestfs-examples.3.html diff --git a/examples/virt-dhcp-address.c b/examples/virt-dhcp-address.c new file mode 100644 index 0000000..c075a47 --- /dev/null +++ b/examples/virt-dhcp-address.c @@ -0,0 +1,342 @@ +/* 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 +#include +#include +#include +#include +#include + +#include +#include + +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 (strcmp (guest_type, "linux") == 0) { + guest_distro = guestfs_inspect_get_distro (g, root); + + 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_egrep (g, "dhclient.*: bound to ", logfile); + 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_unused) +{ + char *system_path; + char tmpfile[] = "/tmp/systemXXXXXX"; + int fd, err; + hive_h *h; + hive_node_h root, node, *nodes; + hive_value_h value; + int32_t dword; + char controlset[] = "ControlSetXXX"; + size_t i; + char *p; + + /* Locate the SYSTEM hive case-sensitive path. */ + system_path = + guestfs_case_sensitive_path (g, "/windows/system32/config/system"); + if (!system_path) { + fprintf (stderr, "virt-dhcp-address: HKLM\\System not found in this guest."); + exit (EXIT_FAILURE); + } + + fd = mkstemp (tmpfile); + if (fd == -1) { + perror ("mkstemp"); + exit (EXIT_FAILURE); + } + + /* Download the SYSTEM hive. */ + if (guestfs_download (g, system_path, tmpfile) == -1) + exit (EXIT_FAILURE); + + free (system_path); + + /* Open the hive to parse it. */ + h = hivex_open (tmpfile, 0); + err = errno; + close (fd); + unlink (tmpfile); + + if (h == NULL) { + errno = err; + perror ("hivex_open"); + exit (EXIT_FAILURE); + } + + /* Navigate to the Select key so we know which ControlSet is in use. */ + root = hivex_root (h); + if (root == 0) { + perror ("hivex_root"); + exit (EXIT_FAILURE); + } + node = hivex_node_get_child (h, root, "Select"); + if (node == 0) { + if (errno != 0) + perror ("hivex_node_get_child"); + else + fprintf (stderr, "virt-dhcp-address: HKLM\\System\\Select key not found."); + exit (EXIT_FAILURE); + } + value = hivex_node_get_value (h, node, "Current"); + if (value == 0) { + if (errno != 0) + perror ("hivex_node_get_value"); + else + fprintf (stderr, "virt-dhcp-address: HKLM\\System\\Select Default entry not found."); + exit (EXIT_FAILURE); + } + /* XXX Should check the type. */ + dword = hivex_value_dword (h, value); + snprintf (controlset, sizeof controlset, "ControlSet%03d", dword); + + /* Get ControlSetXXX\Services\Tcpip\Parameters\Interfaces. */ + const char *path[] = { controlset, "Services", "Tcpip", "Parameters", + "Interfaces" }; + node = root; + errno = 0; + for (i = 0; node != 0 && i < sizeof path / sizeof path[0]; ++i) + node = hivex_node_get_child (h, node, path[i]); + + if (node == 0) { + if (errno != 0) + perror ("hivex_node_get_child"); + else + fprintf (stderr, "virt-dhcp-address: HKLM\\System\\%s\\Services\\Tcpip\\Parameters\\Interfaces not found.", controlset); + exit (EXIT_FAILURE); + } + + /* Look for a node under here which has a "DhcpIPAddress" entry in it. */ + nodes = hivex_node_children (h, node); + if (nodes == NULL) { + perror ("hivex_node_children"); + exit (EXIT_FAILURE); + } + + value = 0; + for (i = 0; value == 0 && nodes[i] != 0; ++i) { + errno = 0; + value = hivex_node_get_value (h, nodes[i], "DhcpIPAddress"); + if (value == 0 && errno != 0) { + perror ("hivex_node_get_value"); + exit (EXIT_FAILURE); + } + } + + if (value == 0) { + fprintf (stderr, "virt-dhcp-address: cannot find DHCP address for this guest.\n"); + exit (EXIT_FAILURE); + } + + /* Get the string and use hivex's auto-conversion to convert it to UTF-8 + * for output. + */ + p = hivex_value_string (h, value); + if (!p) { + perror ("hivex_value_string"); + exit (EXIT_FAILURE); + } + + printf ("%s\n", p); + + /* Close the hive handle. */ + hivex_close (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; +} + +static void +free_strings (char **argv) +{ + size_t i; + + for (i = 0; argv[i]; ++i) + free (argv[i]); + free (argv); +}