Richard W.M. Jones
2010-Nov-10 11:44 UTC
[Libguestfs] [PATCH 0/7] Add libvirt domain to core API
This series of patches aim to make adding disks from libvirt domains easy through the core API. These two new APIs allow you to add the disks from a libvirt domain. The higher level add-domain API takes the name of the libvirt domain as a string and connects to libvirt itself. The lower level add-libvirt-dom API relies on the program to connect to libvirt and pass the virDomainPtr into the API call. int guestfs_add_domain (guestfs_h *g, const char *dom, ...); int guestfs_add_libvirt_dom (guestfs_h *g, virDomainPtr dom, ...); In guestfish you can use the 'domain' command to access the higher level API, eg: ><fs> domain Fedora14 libvirturi:qemu:///system 1 (The returned number is the number of disks that were added.) Since it would be impossible for the user to create a virDomainPtr inside guestfish, the lower-level API is not exposed. The 'guestfish -d' option is reimplemented to use this new API, simplifying guestfish (and guestmount) and meaning that these programs no longer directly depend on libvirt or libxml2. As well as that I wanted libvirt and libxml2 to be optional dependencies. Distro packagers shouldn't need them in order to compile libguestfs. So I took the opportunity to make those optional, and at the same time to make pcre, libmagic and hivex optional. Of course if you don't have those dependencies then certain core API features are disabled. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones New in Fedora 11: Fedora Windows cross-compiler. Compile Windows programs, test, and build Windows installers. Over 70 libraries supprt'd http://fedoraproject.org/wiki/MinGW http://www.annexia.org/fedora_mingw
Richard W.M. Jones
2010-Nov-10 11:45 UTC
[Libguestfs] [PATCH 1/7] lib: Make pcre, libmagic and hivex libraries optional.
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones New in Fedora 11: Fedora Windows cross-compiler. Compile Windows programs, test, and build Windows installers. Over 70 libraries supprt'd http://fedoraproject.org/wiki/MinGW http://www.annexia.org/fedora_mingw -------------- next part -------------->From a0b4caa0821b759de01361b7019c9c9c9607027d Mon Sep 17 00:00:00 2001From: Richard Jones <rjones at redhat.com> Date: Tue, 9 Nov 2010 15:59:40 +0000 Subject: [PATCH 1/7] lib: Make pcre, libmagic and hivex libraries optional. This change makes these libraries optional. If they are not available at compile time then certain core API features will be disabled (see below). This also changes PCRE detection to use pkg-config instead of the ad hoc autoconf checks. The large inspect.c file has been split out into separate function-specific files. file-architecture: requires pcre & libmagic inspection: requires pcre & hivex --- README | 4 +- configure.ac | 45 ++-- po/POTFILES.in | 2 + src/Makefile.am | 6 +- src/filearch.c | 267 +++++++++++++++++++++ src/guestfs-internal.h | 16 ++ src/guestfs.c | 9 + src/inspect.c | 607 +++++++++++++++--------------------------------- src/listfs.c | 169 ++++++++++++++ 9 files changed, 674 insertions(+), 451 deletions(-) create mode 100644 src/filearch.c create mode 100644 src/listfs.c diff --git a/README b/README index b6a7088..8b88b98 100644 --- a/README +++ b/README @@ -47,9 +47,9 @@ Requirements - XDR, rpcgen (on Linux these are provided by glibc) -- pcre (Perl Compatible Regular Expressions C library) +- pcre (Perl Compatible Regular Expressions C library) (optional) -- libmagic (the library that corresponds to the 'file' command) +- libmagic (the library that corresponds to the 'file' command) (optional) - libvirt diff --git a/configure.ac b/configure.ac index 4458d49..e7761c9 100644 --- a/configure.ac +++ b/configure.ac @@ -188,15 +188,6 @@ AC_ARG_ENABLE([appliance], AM_CONDITIONAL([ENABLE_APPLIANCE],[test "x$enable_appliance" = "xyes"]) AC_MSG_RESULT([$enable_appliance]) -dnl Check for PCRE. -AC_CHECK_LIB([pcre],[pcre_compile], - [AC_SUBST([LIBPCRE], ["-lpcre"])], - [AC_MSG_FAILURE( - [Perl Compatible Regular Expressions library (PCRE) is required])]) -AC_CHECK_HEADER([pcre.h],[], - [AC_MSG_FAILURE( - [Perl Compatible Regular Expressions library (PCRE) header file pcre.h is required])]) - dnl Check for rpcgen and XDR library. rpcgen is optional. AC_CHECK_PROG([RPCGEN],[rpcgen],[rpcgen],[no]) AM_CONDITIONAL([HAVE_RPCGEN],[test "x$RPCGEN" != "xno"]) @@ -445,33 +436,43 @@ dnl For i18n. AM_GNU_GETTEXT([external]) AM_GNU_GETTEXT_VERSION([0.17]) -dnl libmagic (required) -AC_CHECK_LIB([magic],[magic_file],[ - AC_SUBST([LIBMAGIC], ["-lmagic"]) - ],[ - AC_MSG_FAILURE([libmagic is required]) - ]) -AC_CHECK_HEADER([magic.h],[],[ - AC_MSG_FAILURE([magic.h header file is required]) - ]) +dnl libmagic (highly recommended) +AC_CHECK_LIB([magic],[magic_file], + [AC_CHECK_HEADER([magic.h], + [AC_SUBST([MAGIC_LIBS], ["-lmagic"]) + AC_DEFINE([HAVE_LIBMAGIC],[1],[libmagic found at compile time.]) + ], []) + ], + [AC_MSG_WARN([libmagic not found, some core features will be disabled])]) dnl libvirt (required) PKG_CHECK_MODULES([LIBVIRT], [libvirt]) AC_SUBST([LIBVIRT_CFLAGS]) AC_SUBST([LIBVIRT_LIBS]) +dnl Check for PCRE (highly recommended) +PKG_CHECK_MODULES([PCRE], [libpcre], + [AC_SUBST([PCRE_CFLAGS]) + AC_SUBST([PCRE_LIBS]) + AC_DEFINE([HAVE_PCRE],[1],[PCRE found at compile time.]) + ], + [AC_MSG_WARN([PCRE not found, some core features will be disabled])]) + dnl libxml2 (required) PKG_CHECK_MODULES([LIBXML2], [libxml-2.0]) AC_SUBST([LIBXML2_CFLAGS]) AC_SUBST([LIBXML2_LIBS]) -dnl hivex library (required) +dnl hivex library (highly recommended) dnl This used to be a part of libguestfs, but was spun off into its dnl own separate upstream project in libguestfs 1.0.85. -PKG_CHECK_MODULES([HIVEX], [hivex]) -AC_SUBST([HIVEX_CFLAGS]) -AC_SUBST([HIVEX_LIBS]) +PKG_CHECK_MODULES([HIVEX], [hivex], + [AC_SUBST([HIVEX_CFLAGS]) + AC_SUBST([HIVEX_LIBS]) + AC_DEFINE([HAVE_HIVEX],[1],[hivex library found at compile time.]) + ], + [AC_MSG_WARN([hivex not found, some core features will be disabled])]) dnl FUSE is optional to build the FUSE module. AC_ARG_ENABLE([fuse], diff --git a/po/POTFILES.in b/po/POTFILES.in index 2013281..e8d9587 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -125,9 +125,11 @@ src/appliance.c src/bindtests.c src/errnostring.c src/errnostring_gperf.c +src/filearch.c src/guestfs.c src/inspect.c src/launch.c +src/listfs.c src/proto.c test-tool/helper.c test-tool/test-tool.c diff --git a/src/Makefile.am b/src/Makefile.am index f23464e..4be61a5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -128,12 +128,14 @@ libguestfs_la_SOURCES = \ actions.c \ appliance.c \ bindtests.c \ + filearch.c \ inspect.c \ launch.c \ + listfs.c \ proto.c \ libguestfs.syms -libguestfs_la_LIBADD = $(HIVEX_LIBS) $(AUGEAS_LIBS) $(LIBPCRE) $(LIBMAGIC) $(LTLIBTHREAD) ../gnulib/lib/libgnu.la +libguestfs_la_LIBADD = $(HIVEX_LIBS) $(AUGEAS_LIBS) $(PCRE_LIBS) $(MAGIC_LIBS) $(LTLIBTHREAD) ../gnulib/lib/libgnu.la # Make libguestfs include the convenience libraries. noinst_LTLIBRARIES = liberrnostring.la libprotocol.la @@ -141,7 +143,7 @@ libguestfs_la_LIBADD += liberrnostring.la libprotocol.la libguestfs_la_CFLAGS = \ -DGUESTFS_DEFAULT_PATH='"$(libdir)/guestfs"' \ - $(HIVEX_CFLAGS) $(AUGEAS_CFLAGS) \ + $(HIVEX_CFLAGS) $(AUGEAS_CFLAGS) $(PCRE_CFLAGS) \ $(WARN_CFLAGS) $(WERROR_CFLAGS) libguestfs_la_CPPFLAGS = -I$(top_srcdir)/gnulib/lib diff --git a/src/filearch.c b/src/filearch.c new file mode 100644 index 0000000..35a2ceb --- /dev/null +++ b/src/filearch.c @@ -0,0 +1,267 @@ +/* libguestfs + * Copyright (C) 2010 Red Hat Inc. + * + * 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 + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <unistd.h> +#include <string.h> +#include <sys/stat.h> + +#ifdef HAVE_PCRE +#include <pcre.h> +#endif +#ifdef HAVE_LIBMAGIC +#include <magic.h> +#endif + +#include "ignore-value.h" + +#include "guestfs.h" +#include "guestfs-internal.h" +#include "guestfs-internal-actions.h" +#include "guestfs_protocol.h" + +#if defined(HAVE_PCRE) && defined(HAVE_LIBMAGIC) + +static pcre *re_file_elf; +static pcre *re_elf_ppc64; + +static void compile_regexps (void) __attribute__((constructor)); +static void free_regexps (void) __attribute__((destructor)); + +static void +compile_regexps (void) +{ + const char *err; + int offset; + +#define COMPILE(re,pattern,options) \ + do { \ + re = pcre_compile ((pattern), (options), &err, &offset, NULL); \ + if (re == NULL) { \ + ignore_value (write (2, err, strlen (err))); \ + abort (); \ + } \ + } while (0) + + COMPILE (re_file_elf, + "ELF.*(?:executable|shared object|relocatable), (.+?),", 0); + COMPILE (re_elf_ppc64, "64.*PowerPC", 0); +} + +static void +free_regexps (void) +{ + pcre_free (re_file_elf); + pcre_free (re_elf_ppc64); +} + +/* Convert output from 'file' command on ELF files to the canonical + * architecture string. Caller must free the result. + */ +static char * +canonical_elf_arch (guestfs_h *g, const char *elf_arch) +{ + const char *r; + + if (strstr (elf_arch, "Intel 80386")) + r = "i386"; + else if (strstr (elf_arch, "Intel 80486")) + r = "i486"; + else if (strstr (elf_arch, "x86-64")) + r = "x86_64"; + else if (strstr (elf_arch, "AMD x86-64")) + r = "x86_64"; + else if (strstr (elf_arch, "SPARC32")) + r = "sparc"; + else if (strstr (elf_arch, "SPARC V9")) + r = "sparc64"; + else if (strstr (elf_arch, "IA-64")) + r = "ia64"; + else if (match (g, elf_arch, re_elf_ppc64)) + r = "ppc64"; + else if (strstr (elf_arch, "PowerPC")) + r = "ppc"; + else + r = elf_arch; + + char *ret = safe_strdup (g, r); + return ret; +} + +static int +is_regular_file (const char *filename) +{ + struct stat statbuf; + + return lstat (filename, &statbuf) == 0 && S_ISREG (statbuf.st_mode); +} + +/* Download and uncompress the cpio file to find binaries within. + * Notes: + * (1) Two lists must be identical. + * (2) Implicit limit of 31 bytes for length of each element (see code + * below). + */ +#define INITRD_BINARIES1 "bin/ls bin/rm bin/modprobe sbin/modprobe bin/sh bin/bash bin/dash bin/nash" +#define INITRD_BINARIES2 {"bin/ls", "bin/rm", "bin/modprobe", "sbin/modprobe", "bin/sh", "bin/bash", "bin/dash", "bin/nash"} + +static char * +cpio_arch (guestfs_h *g, const char *file, const char *path) +{ + TMP_TEMPLATE_ON_STACK (dir); +#define dir_len (strlen (dir)) +#define initrd_len (dir_len + 16) + char initrd[initrd_len]; +#define cmd_len (dir_len + 256) + char cmd[cmd_len]; +#define bin_len (dir_len + 32) + char bin[bin_len]; + + char *ret = NULL; + + const char *method; + if (strstr (file, "gzip")) + method = "zcat"; + else if (strstr (file, "bzip2")) + method = "bzcat"; + else + method = "cat"; + + if (mkdtemp (dir) == NULL) { + perrorf (g, "mkdtemp"); + goto out; + } + + snprintf (initrd, initrd_len, "%s/initrd", dir); + if (guestfs_download (g, path, initrd) == -1) + goto out; + + snprintf (cmd, cmd_len, + "cd %s && %s initrd | cpio --quiet -id " INITRD_BINARIES1, + dir, method); + int r = system (cmd); + if (r == -1 || WEXITSTATUS (r) != 0) { + perrorf (g, "cpio command failed"); + goto out; + } + + const char *bins[] = INITRD_BINARIES2; + size_t i; + for (i = 0; i < sizeof bins / sizeof bins[0]; ++i) { + snprintf (bin, bin_len, "%s/%s", dir, bins[i]); + + if (is_regular_file (bin)) { + int flags = g->verbose ? MAGIC_DEBUG : 0; + flags |= MAGIC_ERROR | MAGIC_RAW; + + magic_t m = magic_open (flags); + if (m == NULL) { + perrorf (g, "magic_open"); + goto out; + } + + if (magic_load (m, NULL) == -1) { + perrorf (g, "magic_load: default magic database file"); + magic_close (m); + goto out; + } + + const char *line = magic_file (m, bin); + if (line == NULL) { + perrorf (g, "magic_file: %s", bin); + magic_close (m); + goto out; + } + + char *elf_arch; + if ((elf_arch = match1 (g, line, re_file_elf)) != NULL) { + ret = canonical_elf_arch (g, elf_arch); + free (elf_arch); + magic_close (m); + goto out; + } + magic_close (m); + } + } + error (g, "file_architecture: could not determine architecture of cpio archive"); + + out: + /* Free up the temporary directory. Note the directory name cannot + * contain shell meta-characters because of the way it was + * constructed above. + */ + snprintf (cmd, cmd_len, "rm -rf %s", dir); + ignore_value (system (cmd)); + + return ret; +#undef dir_len +#undef initrd_len +#undef cmd_len +#undef bin_len +} + +char * +guestfs__file_architecture (guestfs_h *g, const char *path) +{ + char *file = NULL; + char *elf_arch = NULL; + char *ret = NULL; + + /* Get the output of the "file" command. Note that because this + * runs in the daemon, LANG=C so it's in English. + */ + file = guestfs_file (g, path); + if (file == NULL) + return NULL; + + if ((elf_arch = match1 (g, file, re_file_elf)) != NULL) + ret = canonical_elf_arch (g, elf_arch); + else if (strstr (file, "PE32 executable")) + ret = safe_strdup (g, "i386"); + else if (strstr (file, "PE32+ executable")) + ret = safe_strdup (g, "x86_64"); + else if (strstr (file, "cpio archive")) + ret = cpio_arch (g, file, path); + else + error (g, "file_architecture: unknown architecture: %s", path); + + free (file); + free (elf_arch); + return ret; /* caller frees */ +} + +#else /* no PCRE or libmagic at compile time */ + +/* XXX Should be an optgroup. */ + +#define NOT_IMPL(r) \ + error (g, _("file-architecture API not available since this version of libguestfs was compiled without PCRE or libmagic libraries")); \ + return r + +char * +guestfs__file_architecture (guestfs_h *g, const char *path) +{ + NOT_IMPL(NULL); +} + +#endif /* no PCRE or libmagic at compile time */ diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h index 9c7c96c..067251c 100644 --- a/src/guestfs-internal.h +++ b/src/guestfs-internal.h @@ -19,6 +19,10 @@ #ifndef GUESTFS_INTERNAL_H_ #define GUESTFS_INTERNAL_H_ +#ifdef HAVE_PCRE +#include <pcre.h> +#endif + #define STREQ(a,b) (strcmp((a),(b)) == 0) #define STRCASEEQ(a,b) (strcasecmp((a),(b)) == 0) #define STRNEQ(a,b) (strcmp((a),(b)) != 0) @@ -223,6 +227,13 @@ extern int guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void ** extern int guestfs___accept_from_daemon (guestfs_h *g); extern int guestfs___build_appliance (guestfs_h *g, char **kernel, char **initrd, char **appliance); extern void guestfs___print_BufferIn (FILE *out, const char *buf, size_t buf_size); +#ifdef HAVE_PCRE +extern int guestfs___match (guestfs_h *g, const char *str, const pcre *re); +extern char *guestfs___match1 (guestfs_h *g, const char *str, const pcre *re); +extern int guestfs___match2 (guestfs_h *g, const char *str, const pcre *re, char **ret1, char **ret2); +#endif +extern int guestfs___feature_available (guestfs_h *g, const char *feature); +extern void guestfs___free_string_list (char **); #define error(g,...) guestfs_error_errno((g),0,__VA_ARGS__) #define perrorf guestfs_perrorf @@ -232,5 +243,10 @@ extern void guestfs___print_BufferIn (FILE *out, const char *buf, size_t buf_siz #define safe_strdup guestfs_safe_strdup #define safe_strndup guestfs_safe_strndup #define safe_memdup guestfs_safe_memdup +#ifdef HAVE_PCRE +#define match guestfs___match +#define match1 guestfs___match1 +#define match2 guestfs___match2 +#endif #endif /* GUESTFS_INTERNAL_H_ */ diff --git a/src/guestfs.c b/src/guestfs.c index e4f74e0..b151226 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -774,3 +774,12 @@ guestfs___print_BufferIn (FILE *out, const char *buf, size_t buf_size) fprintf (out, _("<truncated, original size %zu bytes>"), orig_size); } + +void +guestfs___free_string_list (char **argv) +{ + size_t i; + for (i = 0; argv[i] != NULL; ++i) + free (argv[i]); + free (argv); +} diff --git a/src/inspect.c b/src/inspect.c index 531e3f7..2b61a75 100644 --- a/src/inspect.c +++ b/src/inspect.c @@ -26,9 +26,13 @@ #include <string.h> #include <sys/stat.h> +#ifdef HAVE_PCRE #include <pcre.h> -#include <magic.h> +#endif + +#ifdef HAVE_HIVEX #include <hivex.h> +#endif #include "c-ctype.h" #include "ignore-value.h" @@ -39,13 +43,13 @@ #include "guestfs-internal-actions.h" #include "guestfs_protocol.h" +#if defined(HAVE_PCRE) && defined(HAVE_HIVEX) + /* Compile all the regular expressions once when the shared library is * loaded. PCRE is thread safe so we're supposedly OK here if * multiple threads call into the libguestfs API functions below * simultaneously. */ -static pcre *re_file_elf; -static pcre *re_elf_ppc64; static pcre *re_fedora; static pcre *re_rhel_old; static pcre *re_rhel; @@ -73,9 +77,6 @@ compile_regexps (void) } \ } while (0) - COMPILE (re_file_elf, - "ELF.*(?:executable|shared object|relocatable), (.+?),", 0); - COMPILE (re_elf_ppc64, "64.*PowerPC", 0); COMPILE (re_fedora, "Fedora release (\\d+)", 0); COMPILE (re_rhel_old, "(?:Red Hat Enterprise Linux|CentOS|Scientific Linux).*release (\\d+).*Update (\\d+)", 0); @@ -92,8 +93,6 @@ compile_regexps (void) static void free_regexps (void) { - pcre_free (re_file_elf); - pcre_free (re_elf_ppc64); pcre_free (re_fedora); pcre_free (re_rhel_old); pcre_free (re_rhel); @@ -104,252 +103,7 @@ free_regexps (void) pcre_free (re_windows_version); } -/* Match a regular expression which contains no captures. Returns - * true if it matches or false if it doesn't. - */ -static int -match (guestfs_h *g, const char *str, const pcre *re) -{ - size_t len = strlen (str); - int vec[30], r; - - r = pcre_exec (re, NULL, str, len, 0, 0, vec, sizeof vec / sizeof vec[0]); - if (r == PCRE_ERROR_NOMATCH) - return 0; - if (r != 1) { - /* Internal error -- should not happen. */ - fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n", - __FILE__, __func__, r, str); - return 0; - } - - return 1; -} - -/* Match a regular expression which contains exactly one capture. If - * the string matches, return the capture, otherwise return NULL. The - * caller must free the result. - */ -static char * -match1 (guestfs_h *g, const char *str, const pcre *re) -{ - size_t len = strlen (str); - int vec[30], r; - - r = pcre_exec (re, NULL, str, len, 0, 0, vec, sizeof vec / sizeof vec[0]); - if (r == PCRE_ERROR_NOMATCH) - return NULL; - if (r != 2) { - /* Internal error -- should not happen. */ - fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n", - __FILE__, __func__, r, str); - return NULL; - } - - return safe_strndup (g, &str[vec[2]], vec[3]-vec[2]); -} - -/* Match a regular expression which contains exactly two captures. */ -static int -match2 (guestfs_h *g, const char *str, const pcre *re, char **ret1, char **ret2) -{ - size_t len = strlen (str); - int vec[30], r; - - r = pcre_exec (re, NULL, str, len, 0, 0, vec, 30); - if (r == PCRE_ERROR_NOMATCH) - return 0; - if (r != 3) { - /* Internal error -- should not happen. */ - fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n", - __FILE__, __func__, r, str); - return 0; - } - - *ret1 = safe_strndup (g, &str[vec[2]], vec[3]-vec[2]); - *ret2 = safe_strndup (g, &str[vec[4]], vec[5]-vec[4]); - - return 1; -} - -/* Convert output from 'file' command on ELF files to the canonical - * architecture string. Caller must free the result. - */ -static char * -canonical_elf_arch (guestfs_h *g, const char *elf_arch) -{ - const char *r; - - if (strstr (elf_arch, "Intel 80386")) - r = "i386"; - else if (strstr (elf_arch, "Intel 80486")) - r = "i486"; - else if (strstr (elf_arch, "x86-64")) - r = "x86_64"; - else if (strstr (elf_arch, "AMD x86-64")) - r = "x86_64"; - else if (strstr (elf_arch, "SPARC32")) - r = "sparc"; - else if (strstr (elf_arch, "SPARC V9")) - r = "sparc64"; - else if (strstr (elf_arch, "IA-64")) - r = "ia64"; - else if (match (g, elf_arch, re_elf_ppc64)) - r = "ppc64"; - else if (strstr (elf_arch, "PowerPC")) - r = "ppc"; - else - r = elf_arch; - - char *ret = safe_strdup (g, r); - return ret; -} - -static int -is_regular_file (const char *filename) -{ - struct stat statbuf; - - return lstat (filename, &statbuf) == 0 && S_ISREG (statbuf.st_mode); -} - -/* Download and uncompress the cpio file to find binaries within. - * Notes: - * (1) Two lists must be identical. - * (2) Implicit limit of 31 bytes for length of each element (see code - * below). - */ -#define INITRD_BINARIES1 "bin/ls bin/rm bin/modprobe sbin/modprobe bin/sh bin/bash bin/dash bin/nash" -#define INITRD_BINARIES2 {"bin/ls", "bin/rm", "bin/modprobe", "sbin/modprobe", "bin/sh", "bin/bash", "bin/dash", "bin/nash"} - -static char * -cpio_arch (guestfs_h *g, const char *file, const char *path) -{ - TMP_TEMPLATE_ON_STACK (dir); -#define dir_len (strlen (dir)) -#define initrd_len (dir_len + 16) - char initrd[initrd_len]; -#define cmd_len (dir_len + 256) - char cmd[cmd_len]; -#define bin_len (dir_len + 32) - char bin[bin_len]; - - char *ret = NULL; - - const char *method; - if (strstr (file, "gzip")) - method = "zcat"; - else if (strstr (file, "bzip2")) - method = "bzcat"; - else - method = "cat"; - - if (mkdtemp (dir) == NULL) { - perrorf (g, "mkdtemp"); - goto out; - } - - snprintf (initrd, initrd_len, "%s/initrd", dir); - if (guestfs_download (g, path, initrd) == -1) - goto out; - - snprintf (cmd, cmd_len, - "cd %s && %s initrd | cpio --quiet -id " INITRD_BINARIES1, - dir, method); - int r = system (cmd); - if (r == -1 || WEXITSTATUS (r) != 0) { - perrorf (g, "cpio command failed"); - goto out; - } - - const char *bins[] = INITRD_BINARIES2; - size_t i; - for (i = 0; i < sizeof bins / sizeof bins[0]; ++i) { - snprintf (bin, bin_len, "%s/%s", dir, bins[i]); - - if (is_regular_file (bin)) { - int flags = g->verbose ? MAGIC_DEBUG : 0; - flags |= MAGIC_ERROR | MAGIC_RAW; - - magic_t m = magic_open (flags); - if (m == NULL) { - perrorf (g, "magic_open"); - goto out; - } - - if (magic_load (m, NULL) == -1) { - perrorf (g, "magic_load: default magic database file"); - magic_close (m); - goto out; - } - - const char *line = magic_file (m, bin); - if (line == NULL) { - perrorf (g, "magic_file: %s", bin); - magic_close (m); - goto out; - } - - char *elf_arch; - if ((elf_arch = match1 (g, line, re_file_elf)) != NULL) { - ret = canonical_elf_arch (g, elf_arch); - free (elf_arch); - magic_close (m); - goto out; - } - magic_close (m); - } - } - error (g, "file_architecture: could not determine architecture of cpio archive"); - - out: - /* Free up the temporary directory. Note the directory name cannot - * contain shell meta-characters because of the way it was - * constructed above. - */ - snprintf (cmd, cmd_len, "rm -rf %s", dir); - ignore_value (system (cmd)); - - return ret; -#undef dir_len -#undef initrd_len -#undef cmd_len -#undef bin_len -} - -char * -guestfs__file_architecture (guestfs_h *g, const char *path) -{ - char *file = NULL; - char *elf_arch = NULL; - char *ret = NULL; - - /* Get the output of the "file" command. Note that because this - * runs in the daemon, LANG=C so it's in English. - */ - file = guestfs_file (g, path); - if (file == NULL) - return NULL; - - if ((elf_arch = match1 (g, file, re_file_elf)) != NULL) - ret = canonical_elf_arch (g, elf_arch); - else if (strstr (file, "PE32 executable")) - ret = safe_strdup (g, "i386"); - else if (strstr (file, "PE32+ executable")) - ret = safe_strdup (g, "x86_64"); - else if (strstr (file, "cpio archive")) - ret = cpio_arch (g, file, path); - else - error (g, "file_architecture: unknown architecture: %s", path); - - free (file); - free (elf_arch); - return ret; /* caller frees */ -} - /* The main inspection code. */ -static int feature_available (guestfs_h *g, const char *feature); -static void free_string_list (char **); static int check_for_filesystem_on (guestfs_h *g, const char *device); char ** @@ -374,12 +128,12 @@ guestfs__inspect_os (guestfs_h *g) size_t i; for (i = 0; devices[i] != NULL; ++i) { if (check_for_filesystem_on (g, devices[i]) == -1) { - free_string_list (devices); + guestfs___free_string_list (devices); guestfs___free_inspect_info (g); return NULL; } } - free_string_list (devices); + guestfs___free_string_list (devices); /* Look at all partitions. */ char **partitions; @@ -391,15 +145,15 @@ guestfs__inspect_os (guestfs_h *g) for (i = 0; partitions[i] != NULL; ++i) { if (check_for_filesystem_on (g, partitions[i]) == -1) { - free_string_list (partitions); + guestfs___free_string_list (partitions); guestfs___free_inspect_info (g); return NULL; } } - free_string_list (partitions); + guestfs___free_string_list (partitions); /* Look at all LVs. */ - if (feature_available (g, "lvm2")) { + if (guestfs___feature_available (g, "lvm2")) { char **lvs; lvs = guestfs_lvs (g); if (lvs == NULL) { @@ -409,12 +163,12 @@ guestfs__inspect_os (guestfs_h *g) for (i = 0; lvs[i] != NULL; ++i) { if (check_for_filesystem_on (g, lvs[i]) == -1) { - free_string_list (lvs); + guestfs___free_string_list (lvs); guestfs___free_inspect_info (g); return NULL; } } - free_string_list (lvs); + guestfs___free_string_list (lvs); } /* At this point we have, in the handle, a list of all filesystems @@ -428,54 +182,6 @@ guestfs__inspect_os (guestfs_h *g) return ret; } -void -guestfs___free_inspect_info (guestfs_h *g) -{ - size_t i; - for (i = 0; i < g->nr_fses; ++i) { - free (g->fses[i].device); - free (g->fses[i].product_name); - free (g->fses[i].arch); - free (g->fses[i].windows_systemroot); - size_t j; - for (j = 0; j < g->fses[i].nr_fstab; ++j) { - free (g->fses[i].fstab[j].device); - free (g->fses[i].fstab[j].mountpoint); - } - free (g->fses[i].fstab); - } - free (g->fses); - g->nr_fses = 0; - g->fses = NULL; -} - -static void -free_string_list (char **argv) -{ - size_t i; - for (i = 0; argv[i] != NULL; ++i) - free (argv[i]); - free (argv); -} - -/* In the Perl code this is a public function. */ -static int -feature_available (guestfs_h *g, const char *feature) -{ - /* If there's an error we should ignore it, so to do that we have to - * temporarily replace the error handler with a null one. - */ - guestfs_error_handler_cb old_error_cb = g->error_cb; - g->error_cb = NULL; - - const char *groups[] = { feature, NULL }; - int r = guestfs_available (g, (char * const *) groups); - - g->error_cb = old_error_cb; - - return r == 0 ? 1 : 0; -} - /* Find out if 'device' contains a filesystem. If it does, add * another entry in g->fses. */ @@ -612,7 +318,7 @@ parse_release_file (guestfs_h *g, struct inspect_fs *fs, return -1; if (product_name[0] == NULL) { error (g, "%s: file is empty", release_filename); - free_string_list (product_name); + guestfs___free_string_list (product_name); return -1; } @@ -677,13 +383,13 @@ parse_lsb_release (guestfs_h *g, struct inspect_fs *fs) free (major); if (fs->major_version == -1) { free (minor); - free_string_list (lines); + guestfs___free_string_list (lines); return -1; } fs->minor_version = parse_unsigned_int (g, minor); free (minor); if (fs->minor_version == -1) { - free_string_list (lines); + guestfs___free_string_list (lines); return -1; } } @@ -703,7 +409,7 @@ parse_lsb_release (guestfs_h *g, struct inspect_fs *fs) } } - free_string_list (lines); + guestfs___free_string_list (lines); return r; } @@ -862,7 +568,7 @@ check_fstab (guestfs_h *g, struct inspect_fs *fs) if (lines[0] == NULL) { error (g, "could not parse /etc/fstab or empty file"); - free_string_list (lines); + guestfs___free_string_list (lines); return -1; } @@ -876,14 +582,14 @@ check_fstab (guestfs_h *g, struct inspect_fs *fs) snprintf (augpath, sizeof augpath, "%s/spec", lines[i]); char *spec = guestfs_aug_get (g, augpath); if (spec == NULL) { - free_string_list (lines); + guestfs___free_string_list (lines); return -1; } snprintf (augpath, sizeof augpath, "%s/file", lines[i]); char *mp = guestfs_aug_get (g, augpath); if (mp == NULL) { - free_string_list (lines); + guestfs___free_string_list (lines); free (spec); return -1; } @@ -893,13 +599,13 @@ check_fstab (guestfs_h *g, struct inspect_fs *fs) free (mp); if (r == -1) { - free_string_list (lines); + guestfs___free_string_list (lines); return -1; } } } - free_string_list (lines); + guestfs___free_string_list (lines); return 0; } @@ -1022,7 +728,7 @@ resolve_fstab_device (guestfs_h *g, const char *spec) } free (a1); - free_string_list (devices); + guestfs___free_string_list (devices); } else { /* Didn't match device pattern, return original spec unchanged. */ @@ -1488,139 +1194,190 @@ guestfs__inspect_get_filesystems (guestfs_h *g, const char *root) return ret; } -/* List filesystems. - * - * The current implementation just uses guestfs_vfs_type and doesn't - * try mounting anything, but we reserve the right in future to try - * mounting filesystems. - */ +#else /* no PCRE or hivex at compile time */ + +/* XXX These functions should be in an optgroup. */ -static void remove_from_list (char **list, const char *item); -static void check_with_vfs_type (guestfs_h *g, const char *dev, char ***ret, size_t *ret_size); +#define NOT_IMPL(r) \ + error (g, _("inspection API not available since this version of libguestfs was compiled without PCRE or hivex libraries")); \ + return r char ** -guestfs__list_filesystems (guestfs_h *g) +guestfs__inspect_os (guestfs_h *g) { - size_t i; - char **ret; - size_t ret_size; + NOT_IMPL(NULL); +} - ret = safe_malloc (g, sizeof (char *)); - ret[0] = NULL; - ret_size = 0; +char ** +guestfs__inspect_get_roots (guestfs_h *g) +{ + NOT_IMPL(NULL); +} - /* Look to see if any devices directly contain filesystems - * (RHBZ#590167). However vfs-type will fail to tell us anything - * useful about devices which just contain partitions, so we also - * get the list of partitions and exclude the corresponding devices - * by using part-to-dev. - */ - char **devices; - devices = guestfs_list_devices (g); - if (devices == NULL) { - free_string_list (ret); - return NULL; - } - char **partitions; - partitions = guestfs_list_partitions (g); - if (partitions == NULL) { - free_string_list (devices); - free_string_list (ret); - return NULL; - } +char * +guestfs__inspect_get_type (guestfs_h *g, const char *root) +{ + NOT_IMPL(NULL); +} - for (i = 0; partitions[i] != NULL; ++i) { - char *dev = guestfs_part_to_dev (g, partitions[i]); - if (dev) - remove_from_list (devices, dev); - free (dev); - } +char * +guestfs__inspect_get_arch (guestfs_h *g, const char *root) +{ + NOT_IMPL(NULL); +} - /* Use vfs-type to check for filesystems on devices. */ - for (i = 0; devices[i] != NULL; ++i) - check_with_vfs_type (g, devices[i], &ret, &ret_size); - free_string_list (devices); +char * +guestfs__inspect_get_distro (guestfs_h *g, const char *root) +{ + NOT_IMPL(NULL); +} - /* Use vfs-type to check for filesystems on partitions. */ - for (i = 0; partitions[i] != NULL; ++i) - check_with_vfs_type (g, partitions[i], &ret, &ret_size); - free_string_list (partitions); +int +guestfs__inspect_get_major_version (guestfs_h *g, const char *root) +{ + NOT_IMPL(-1); +} - if (feature_available (g, "lvm2")) { - /* Use vfs-type to check for filesystems on LVs. */ - char **lvs; - lvs = guestfs_lvs (g); - if (lvs == NULL) { - free_string_list (ret); - return NULL; - } +int +guestfs__inspect_get_minor_version (guestfs_h *g, const char *root) +{ + NOT_IMPL(-1); +} - for (i = 0; lvs[i] != NULL; ++i) - check_with_vfs_type (g, lvs[i], &ret, &ret_size); - free_string_list (lvs); - } +char * +guestfs__inspect_get_product_name (guestfs_h *g, const char *root) +{ + NOT_IMPL(NULL); +} - return ret; +char * +guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root) +{ + NOT_IMPL(NULL); } -/* If 'item' occurs in 'list', remove and free it. */ -static void -remove_from_list (char **list, const char *item) +char ** +guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root) { - size_t i; + NOT_IMPL(NULL); +} - for (i = 0; list[i] != NULL; ++i) - if (STREQ (list[i], item)) { - free (list[i]); - for (; list[i+1] != NULL; ++i) - list[i] = list[i+1]; - list[i] = NULL; - return; - } +char ** +guestfs__inspect_get_filesystems (guestfs_h *g, const char *root) +{ + NOT_IMPL(NULL); } -/* Use vfs-type to look for a filesystem of some sort on 'dev'. - * Apart from some types which we ignore, add the result to the - * 'ret' string list. - */ -static void -check_with_vfs_type (guestfs_h *g, const char *device, - char ***ret, size_t *ret_size) +#endif /* no PCRE or hivex at compile time */ + +void +guestfs___free_inspect_info (guestfs_h *g) { - char *v; + size_t i; + for (i = 0; i < g->nr_fses; ++i) { + free (g->fses[i].device); + free (g->fses[i].product_name); + free (g->fses[i].arch); + free (g->fses[i].windows_systemroot); + size_t j; + for (j = 0; j < g->fses[i].nr_fstab; ++j) { + free (g->fses[i].fstab[j].device); + free (g->fses[i].fstab[j].mountpoint); + } + free (g->fses[i].fstab); + } + free (g->fses); + g->nr_fses = 0; + g->fses = NULL; +} +/* In the Perl code this is a public function. */ +int +guestfs___feature_available (guestfs_h *g, const char *feature) +{ + /* If there's an error we should ignore it, so to do that we have to + * temporarily replace the error handler with a null one. + */ guestfs_error_handler_cb old_error_cb = g->error_cb; g->error_cb = NULL; - char *vfs_type = guestfs_vfs_type (g, device); + + const char *groups[] = { feature, NULL }; + int r = guestfs_available (g, (char * const *) groups); + g->error_cb = old_error_cb; - if (!vfs_type) - v = safe_strdup (g, "unknown"); - else { - /* Ignore all "*_member" strings. In libblkid these are returned - * for things which are members of some RAID or LVM set, most - * importantly "LVM2_member" which is a PV. - */ - size_t n = strlen (vfs_type); - if (n >= 7 && STREQ (&vfs_type[n-7], "_member")) { - free (vfs_type); - return; - } + return r == 0 ? 1 : 0; +} - /* Ignore LUKS-encrypted partitions. These are also containers. */ - if (STREQ (vfs_type, "crypto_LUKS")) { - free (vfs_type); - return; - } +#ifdef HAVE_PCRE + +/* Match a regular expression which contains no captures. Returns + * true if it matches or false if it doesn't. + */ +int +guestfs___match (guestfs_h *g, const char *str, const pcre *re) +{ + size_t len = strlen (str); + int vec[30], r; + + r = pcre_exec (re, NULL, str, len, 0, 0, vec, sizeof vec / sizeof vec[0]); + if (r == PCRE_ERROR_NOMATCH) + return 0; + if (r != 1) { + /* Internal error -- should not happen. */ + fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n", + __FILE__, __func__, r, str); + return 0; + } + + return 1; +} + +/* Match a regular expression which contains exactly one capture. If + * the string matches, return the capture, otherwise return NULL. The + * caller must free the result. + */ +char * +guestfs___match1 (guestfs_h *g, const char *str, const pcre *re) +{ + size_t len = strlen (str); + int vec[30], r; - v = vfs_type; + r = pcre_exec (re, NULL, str, len, 0, 0, vec, sizeof vec / sizeof vec[0]); + if (r == PCRE_ERROR_NOMATCH) + return NULL; + if (r != 2) { + /* Internal error -- should not happen. */ + fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n", + __FILE__, __func__, r, str); + return NULL; } - /* Extend the return array. */ - size_t i = *ret_size; - *ret_size += 2; - *ret = safe_realloc (g, *ret, (*ret_size + 1) * sizeof (char *)); - (*ret)[i] = safe_strdup (g, device); - (*ret)[i+1] = v; - (*ret)[i+2] = NULL; + return safe_strndup (g, &str[vec[2]], vec[3]-vec[2]); } + +/* Match a regular expression which contains exactly two captures. */ +int +guestfs___match2 (guestfs_h *g, const char *str, const pcre *re, + char **ret1, char **ret2) +{ + size_t len = strlen (str); + int vec[30], r; + + r = pcre_exec (re, NULL, str, len, 0, 0, vec, 30); + if (r == PCRE_ERROR_NOMATCH) + return 0; + if (r != 3) { + /* Internal error -- should not happen. */ + fprintf (stderr, "libguestfs: %s: %s: internal error: pcre_exec returned unexpected error code %d when matching against the string \"%s\"\n", + __FILE__, __func__, r, str); + return 0; + } + + *ret1 = safe_strndup (g, &str[vec[2]], vec[3]-vec[2]); + *ret2 = safe_strndup (g, &str[vec[4]], vec[5]-vec[4]); + + return 1; +} + +#endif /* HAVE_PCRE */ diff --git a/src/listfs.c b/src/listfs.c new file mode 100644 index 0000000..a89cd9b --- /dev/null +++ b/src/listfs.c @@ -0,0 +1,169 @@ +/* libguestfs + * Copyright (C) 2010 Red Hat Inc. + * + * 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 + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <unistd.h> +#include <string.h> +#include <sys/stat.h> + +#include "guestfs.h" +#include "guestfs-internal.h" +#include "guestfs-internal-actions.h" +#include "guestfs_protocol.h" + +/* List filesystems. + * + * The current implementation just uses guestfs_vfs_type and doesn't + * try mounting anything, but we reserve the right in future to try + * mounting filesystems. + */ + +static void remove_from_list (char **list, const char *item); +static void check_with_vfs_type (guestfs_h *g, const char *dev, char ***ret, size_t *ret_size); + +char ** +guestfs__list_filesystems (guestfs_h *g) +{ + size_t i; + char **ret; + size_t ret_size; + + ret = safe_malloc (g, sizeof (char *)); + ret[0] = NULL; + ret_size = 0; + + /* Look to see if any devices directly contain filesystems + * (RHBZ#590167). However vfs-type will fail to tell us anything + * useful about devices which just contain partitions, so we also + * get the list of partitions and exclude the corresponding devices + * by using part-to-dev. + */ + char **devices; + devices = guestfs_list_devices (g); + if (devices == NULL) { + guestfs___free_string_list (ret); + return NULL; + } + char **partitions; + partitions = guestfs_list_partitions (g); + if (partitions == NULL) { + guestfs___free_string_list (devices); + guestfs___free_string_list (ret); + return NULL; + } + + for (i = 0; partitions[i] != NULL; ++i) { + char *dev = guestfs_part_to_dev (g, partitions[i]); + if (dev) + remove_from_list (devices, dev); + free (dev); + } + + /* Use vfs-type to check for filesystems on devices. */ + for (i = 0; devices[i] != NULL; ++i) + check_with_vfs_type (g, devices[i], &ret, &ret_size); + guestfs___free_string_list (devices); + + /* Use vfs-type to check for filesystems on partitions. */ + for (i = 0; partitions[i] != NULL; ++i) + check_with_vfs_type (g, partitions[i], &ret, &ret_size); + guestfs___free_string_list (partitions); + + if (guestfs___feature_available (g, "lvm2")) { + /* Use vfs-type to check for filesystems on LVs. */ + char **lvs; + lvs = guestfs_lvs (g); + if (lvs == NULL) { + guestfs___free_string_list (ret); + return NULL; + } + + for (i = 0; lvs[i] != NULL; ++i) + check_with_vfs_type (g, lvs[i], &ret, &ret_size); + guestfs___free_string_list (lvs); + } + + return ret; +} + +/* If 'item' occurs in 'list', remove and free it. */ +static void +remove_from_list (char **list, const char *item) +{ + size_t i; + + for (i = 0; list[i] != NULL; ++i) + if (STREQ (list[i], item)) { + free (list[i]); + for (; list[i+1] != NULL; ++i) + list[i] = list[i+1]; + list[i] = NULL; + return; + } +} + +/* Use vfs-type to look for a filesystem of some sort on 'dev'. + * Apart from some types which we ignore, add the result to the + * 'ret' string list. + */ +static void +check_with_vfs_type (guestfs_h *g, const char *device, + char ***ret, size_t *ret_size) +{ + char *v; + + guestfs_error_handler_cb old_error_cb = g->error_cb; + g->error_cb = NULL; + char *vfs_type = guestfs_vfs_type (g, device); + g->error_cb = old_error_cb; + + if (!vfs_type) + v = safe_strdup (g, "unknown"); + else { + /* Ignore all "*_member" strings. In libblkid these are returned + * for things which are members of some RAID or LVM set, most + * importantly "LVM2_member" which is a PV. + */ + size_t n = strlen (vfs_type); + if (n >= 7 && STREQ (&vfs_type[n-7], "_member")) { + free (vfs_type); + return; + } + + /* Ignore LUKS-encrypted partitions. These are also containers. */ + if (STREQ (vfs_type, "crypto_LUKS")) { + free (vfs_type); + return; + } + + v = vfs_type; + } + + /* Extend the return array. */ + size_t i = *ret_size; + *ret_size += 2; + *ret = safe_realloc (g, *ret, (*ret_size + 1) * sizeof (char *)); + (*ret)[i] = safe_strdup (g, device); + (*ret)[i+1] = v; + (*ret)[i+2] = NULL; +} -- 1.7.3.2
Richard W.M. Jones
2010-Nov-10 11:45 UTC
[Libguestfs] [PATCH 2/7] Add internal facility to checkpoint and roll back the command line.
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones New in Fedora 11: Fedora Windows cross-compiler. Compile Windows programs, test, and build Windows installers. Over 70 libraries supprt'd http://fedoraproject.org/wiki/MinGW http://www.annexia.org/fedora_mingw -------------- next part -------------->From 1c29849e0bdc731c023cff00d2c2354a41fd2a92 Mon Sep 17 00:00:00 2001From: Richard Jones <rjones at redhat.com> Date: Tue, 9 Nov 2010 18:50:57 +0000 Subject: [PATCH 2/7] Add internal facility to checkpoint and roll back the command line. This internal interface can be used to ensure that certain operations are atomic. --- src/guestfs-internal.h | 2 ++ src/launch.c | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 0 deletions(-) diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h index 067251c..f8b3e94 100644 --- a/src/guestfs-internal.h +++ b/src/guestfs-internal.h @@ -234,6 +234,8 @@ extern int guestfs___match2 (guestfs_h *g, const char *str, const pcre *re, char #endif extern int guestfs___feature_available (guestfs_h *g, const char *feature); extern void guestfs___free_string_list (char **); +extern int guestfs___checkpoint_cmdline (guestfs_h *g); +extern void guestfs___rollback_cmdline (guestfs_h *g, int pos); #define error(g,...) guestfs_error_errno((g),0,__VA_ARGS__) #define perrorf guestfs_perrorf diff --git a/src/launch.c b/src/launch.c index e5bca56..48ddb8d 100644 --- a/src/launch.c +++ b/src/launch.c @@ -34,6 +34,7 @@ #include <sys/select.h> #include <dirent.h> #include <signal.h> +#include <assert.h> #include <rpc/types.h> #include <rpc/xdr.h> @@ -101,6 +102,25 @@ add_cmdline (guestfs_h *g, const char *str) } int +guestfs___checkpoint_cmdline (guestfs_h *g) +{ + return g->cmdline_size; +} + +void +guestfs___rollback_cmdline (guestfs_h *g, int pos) +{ + int i; + + assert (g->cmdline_size >= pos); + + for (i = g->cmdline_size - 1; i >= pos; --i) + free (g->cmdline[i]); + + g->cmdline_size = pos; +} + +int guestfs__config (guestfs_h *g, const char *qemu_param, const char *qemu_value) { -- 1.7.3.2
Richard W.M. Jones
2010-Nov-10 11:45 UTC
[Libguestfs] [PATCH 3/7] New API: debug-cmdline for printing QEMU command line (internal only).
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://et.redhat.com/~rjones/virt-top -------------- next part -------------->From eaedf025f5c45a4e05cbf25e145215d48bea8f8d Mon Sep 17 00:00:00 2001From: Richard W.M. Jones <rjones at redhat.com> Date: Wed, 10 Nov 2010 10:32:33 +0000 Subject: [PATCH 3/7] New API: debug-cmdline for printing QEMU command line (internal only). This is an internal-only debugging API so may be changed or removed at any time in the future. --- generator/generator_actions.ml | 7 +++++++ src/launch.c | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 0 deletions(-) diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml index b8937d0..a0a337b 100644 --- a/generator/generator_actions.ml +++ b/generator/generator_actions.ml @@ -1051,6 +1051,13 @@ found or the caller has not called C<guestfs_inspect_os>. Please read L<guestfs(3)/INSPECTION> for more details."); + ("debug_cmdline", (RStringList "cmdline", [], []), -1, [NotInDocs], + [], + "debug the QEMU command line (internal use only)", + "\ +This returns the internal QEMU command line. 'debug' commands are +not part of the formal API and can be removed or changed at any time."); + ] (* daemon_functions are any functions which cause some action diff --git a/src/launch.c b/src/launch.c index 48ddb8d..e4f4728 100644 --- a/src/launch.c +++ b/src/launch.c @@ -120,6 +120,30 @@ guestfs___rollback_cmdline (guestfs_h *g, int pos) g->cmdline_size = pos; } +/* Internal command to return the command line. */ +char ** +guestfs__debug_cmdline (guestfs_h *g) +{ + int i; + char **r; + + if (g->cmdline == NULL) { + r = safe_malloc (g, sizeof (char *) * 1); + r[0] = NULL; + return r; + } + + r = safe_malloc (g, sizeof (char *) * (g->cmdline_size + 1)); + r[0] = safe_strdup (g, g->qemu); /* g->cmdline[0] is always NULL */ + + for (i = 1; i < g->cmdline_size; ++i) + r[i] = safe_strdup (g, g->cmdline[i]); + + r[g->cmdline_size] = NULL; + + return r; /* caller frees */ +} + int guestfs__config (guestfs_h *g, const char *qemu_param, const char *qemu_value) -- 1.7.3.2
Richard W.M. Jones
2010-Nov-10 11:46 UTC
[Libguestfs] [PATCH 4/7] generator: Add Pointer parameter type to the generator.
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones New in Fedora 11: Fedora Windows cross-compiler. Compile Windows programs, test, and build Windows installers. Over 70 libraries supprt'd http://fedoraproject.org/wiki/MinGW http://www.annexia.org/fedora_mingw -------------- next part -------------->From 4ada0a7815075c9cbe9d8b00da791c105ae739a9 Mon Sep 17 00:00:00 2001From: Richard Jones <rjones at redhat.com> Date: Tue, 9 Nov 2010 12:08:06 +0000 Subject: [PATCH 4/7] generator: Add Pointer parameter type to the generator. This allows generic "foo *bar" pointers to be passed to library functions (not to daemon functions). In the language bindings (except Perl) these are handled as generic int64s with the assumption being that any pointer can be converted to and from this. There is room to add specific support for some pointer types in future by specializing the match cases. However this is inherently tricky because it depends on the implementation details of other bindings (eg. to support virDomainPtr in OCaml depends on the implementation details of the ocaml-libvirt project). Perl is slightly different in that you have to supply a typemap. Again this would depend on the implementation detail of an external library unless you supplied a generic typemap for int64. --- generator/generator_bindtests.ml | 1 + generator/generator_c.ml | 9 ++++++++- generator/generator_capitests.ml | 4 ++++ generator/generator_checks.ml | 13 +++++++++++++ generator/generator_csharp.ml | 4 ++-- generator/generator_daemon.ml | 2 ++ generator/generator_fish.ml | 4 ++++ generator/generator_haskell.ml | 5 +++-- generator/generator_java.ml | 15 ++++++++++----- generator/generator_ocaml.ml | 9 ++++++--- generator/generator_perl.ml | 5 +++-- generator/generator_php.ml | 10 +++++----- generator/generator_python.ml | 16 ++++++++++++---- generator/generator_ruby.ml | 4 +++- generator/generator_types.ml | 17 +++++++++++++++++ generator/generator_utils.ml | 2 +- generator/generator_xdr.ml | 1 + 17 files changed, 95 insertions(+), 26 deletions(-) diff --git a/generator/generator_bindtests.ml b/generator/generator_bindtests.ml index 02b0680..ebe2e24 100644 --- a/generator/generator_bindtests.ml +++ b/generator/generator_bindtests.ml @@ -89,6 +89,7 @@ print_strings (char *const *argv) | Bool n -> pr " printf (\"%%s\\n\", %s ? \"true\" : \"false\");\n" n | Int n -> pr " printf (\"%%d\\n\", %s);\n" n | Int64 n -> pr " printf (\"%%\" PRIi64 \"\\n\", %s);\n" n + | Pointer _ -> assert false ) args; pr " /* Java changes stdout line buffering so we need this: */\n"; pr " fflush (stdout);\n"; diff --git a/generator/generator_c.ml b/generator/generator_c.ml index 4480200..a2f40da 100644 --- a/generator/generator_c.ml +++ b/generator/generator_c.ml @@ -116,6 +116,9 @@ let rec generate_prototype ?(extern = true) ?(static = false) pr "const char *%s" n; next (); pr "size_t %s_size" n + | Pointer (t, n) -> + next (); + pr "%s %s" t n ) args; if is_RBufferOut then (next (); pr "size_t *size_r"); if optargs <> [] then ( @@ -536,7 +539,8 @@ check_state (guestfs_h *g, const char *caller) | BufferIn n | StringList n | DeviceList n - | Key n -> + | Key n + | Pointer (_, n) -> pr " if (%s == NULL) {\n" n; pr " error (g, \"%%s: %%s: parameter cannot be NULL\",\n"; pr " \"%s\", \"%s\");\n" shortname n; @@ -639,6 +643,8 @@ check_state (guestfs_h *g, const char *caller) | BufferIn n -> (* RHBZ#646822 *) pr " fputc (' ', stderr);\n"; pr " guestfs___print_BufferIn (stderr, %s, %s_size);\n" n n + | Pointer (t, n) -> + pr " fprintf (stderr, \" (%s)%%p\", %s);\n" t n ) args; (* Optional arguments. *) @@ -770,6 +776,7 @@ check_state (guestfs_h *g, const char *caller) pr " }\n"; pr " args.%s.%s_val = (char *) %s;\n" n n n; pr " args.%s.%s_len = %s_size;\n" n n n + | Pointer _ -> assert false ) args; pr " serial = guestfs___send (g, GUESTFS_PROC_%s,\n" (String.uppercase shortname); diff --git a/generator/generator_capitests.ml b/generator/generator_capitests.ml index 325b37c..190e10f 100644 --- a/generator/generator_capitests.ml +++ b/generator/generator_capitests.ml @@ -744,6 +744,9 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd ) strs; pr " NULL\n"; pr " };\n"; + | Pointer _, _ -> + (* Difficult to make these pointers in order to run a test. *) + assert false ) (List.combine (snd style) args); let error_code @@ -799,6 +802,7 @@ and generate_test_command_call ?(expect_error = false) ?test test_name cmd pr ", %Ld" i | Bool _, arg -> let b = bool_of_string arg in pr ", %d" (if b then 1 else 0) + | Pointer _, _ -> assert false ) (List.combine (snd style) args); (match fst style with diff --git a/generator/generator_checks.ml b/generator/generator_checks.ml index 3474047..0fb8ea4 100644 --- a/generator/generator_checks.ml +++ b/generator/generator_checks.ml @@ -129,6 +129,19 @@ let () ) optargs ) all_functions; + (* Some parameter types not supported for daemon functions. *) + List.iter ( + fun (name, (_, args, optargs), _, _, _, _, _) -> + let check_arg_type = function + | Pointer _ -> + failwithf "Pointer is not supported for daemon function %s." + name + | _ -> () + in + List.iter check_arg_type args; + List.iter check_arg_type optargs; + ) daemon_functions; + (* Check short descriptions. *) List.iter ( fun (name, _, _, _, _, shortdesc, _) -> diff --git a/generator/generator_csharp.ml b/generator/generator_csharp.ml index e178945..5cc71c3 100644 --- a/generator/generator_csharp.ml +++ b/generator/generator_csharp.ml @@ -197,7 +197,7 @@ namespace Guestfs pr ", bool %s" n | Int n -> pr ", int %s" n - | Int64 n -> + | Int64 n | Pointer (_, n) -> pr ", long %s" n ) args; pr ");\n" @@ -222,7 +222,7 @@ namespace Guestfs next (); pr "bool %s" n | Int n -> next (); pr "int %s" n - | Int64 n -> + | Int64 n | Pointer (_, n) -> next (); pr "long %s" n ) args; pr ")\n" diff --git a/generator/generator_daemon.ml b/generator/generator_daemon.ml index df9f965..e3d87e5 100644 --- a/generator/generator_daemon.ml +++ b/generator/generator_daemon.ml @@ -105,6 +105,7 @@ and generate_daemon_actions () | BufferIn n -> pr " const char *%s;\n" n; pr " size_t %s_size;\n" n + | Pointer _ -> assert false ) args ); pr "\n"; @@ -174,6 +175,7 @@ and generate_daemon_actions () | BufferIn n -> pr " %s = args.%s.%s_val;\n" n n n; pr " %s_size = args.%s.%s_len;\n" n n n + | Pointer _ -> assert false ) args; pr "\n" ); diff --git a/generator/generator_fish.ml b/generator/generator_fish.ml index 76b4c7a..f59c520 100644 --- a/generator/generator_fish.ml +++ b/generator/generator_fish.ml @@ -323,6 +323,7 @@ Guestfish will prompt for these separately." | Bool n -> pr " int %s;\n" n | Int n -> pr " int %s;\n" n | Int64 n -> pr " int64_t %s;\n" n + | Pointer _ -> assert false ) args; if optargs <> [] then ( @@ -423,6 +424,7 @@ Guestfish will prompt for these separately." parse_integer "argv[i++]" "xstrtoll" "long long" "int" range name | Int64 name -> parse_integer "argv[i++]" "xstrtoll" "long long" "int64_t" None name + | Pointer _ -> assert false ) args; (* Optional arguments are prefixed with <argname>:<value> and @@ -506,6 +508,7 @@ Guestfish will prompt for these separately." pr " free_file_in (%s);\n" name | StringList name | DeviceList name -> pr " free_strings (%s);\n" name + | Pointer _ -> assert false ) args; (* Any output flags? *) @@ -810,6 +813,7 @@ and generate_fish_actions_pod () | FileIn n | FileOut n -> pr " (%s|-)" n | BufferIn n -> pr " %s" n | Key _ -> () (* keys are entered at a prompt *) + | Pointer _ -> assert false ) args; List.iter ( function diff --git a/generator/generator_haskell.ml b/generator/generator_haskell.ml index b49e385..88e4f7f 100644 --- a/generator/generator_haskell.ml +++ b/generator/generator_haskell.ml @@ -148,7 +148,7 @@ last_error h = do pr "withCStringLen %s $ \\(%s, %s_size) -> " n n n | OptString n -> pr "maybeWith withCString %s $ \\%s -> " n n | StringList n | DeviceList n -> pr "withMany withCString %s $ \\%s -> withArray0 nullPtr %s $ \\%s -> " n n n n - | Bool _ | Int _ | Int64 _ -> () + | Bool _ | Int _ | Int64 _ | Pointer _ -> () ) args; (* Convert integer arguments. *) let args @@ -156,7 +156,7 @@ last_error h = do function | Bool n -> sprintf "(fromBool %s)" n | Int n -> sprintf "(fromIntegral %s)" n - | Int64 n -> sprintf "(fromIntegral %s)" n + | Int64 n | Pointer (_, n) -> sprintf "(fromIntegral %s)" n | FileIn n | FileOut n | Pathname n | Device n | Dev_or_Path n | String n | OptString n @@ -222,6 +222,7 @@ and generate_haskell_prototype ~handle ?(hs = false) (ret, args, optargs) | Bool _ -> pr "%s" bool | Int _ -> pr "%s" int | Int64 _ -> pr "%s" int + | Pointer _ -> pr "%s" int | FileIn _ -> pr "%s" string | FileOut _ -> pr "%s" string ); diff --git a/generator/generator_java.ml b/generator/generator_java.ml index b551740..5ac92f7 100644 --- a/generator/generator_java.ml +++ b/generator/generator_java.ml @@ -217,7 +217,7 @@ and generate_java_prototype ?(public=false) ?(privat=false) ?(native=false) pr "boolean %s" n | Int n -> pr "int %s" n - | Int64 n -> + | Int64 n | Pointer (_, n) -> pr "long %s" n ) args; @@ -347,7 +347,7 @@ Java_com_redhat_et_libguestfs_GuestFS__1close pr ", jboolean j%s" n | Int n -> pr ", jint j%s" n - | Int64 n -> + | Int64 n | Pointer (_, n) -> pr ", jlong j%s" n ) args; if optargs <> [] then @@ -410,6 +410,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close pr " int %s;\n" n | Int64 n -> pr " int64_t %s;\n" n + | Pointer (t, n) -> + pr " %s %s;\n" t n ) args; let needs_i @@ -458,6 +460,8 @@ Java_com_redhat_et_libguestfs_GuestFS__1close | Int n | Int64 n -> pr " %s = j%s;\n" n n + | Pointer (t, n) -> + pr " %s = (%s) j%s;\n" n t n ) args; if optargs <> [] then ( @@ -497,9 +501,10 @@ Java_com_redhat_et_libguestfs_GuestFS__1close pr " (*env)->ReleaseStringUTFChars (env, o, %s[i]);\n" n; pr " }\n"; pr " free (%s);\n" n - | Bool n - | Int n - | Int64 n -> () + | Bool _ + | Int _ + | Int64 _ + | Pointer _ -> () ) args; (* Check for errors. *) diff --git a/generator/generator_ocaml.ml b/generator/generator_ocaml.ml index 888a152..860242c 100644 --- a/generator/generator_ocaml.ml +++ b/generator/generator_ocaml.ml @@ -198,6 +198,7 @@ and generate_ocaml_c () #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <stdint.h> #include <caml/config.h> #include <caml/alloc.h> @@ -400,6 +401,8 @@ copy_table (char * const * argv) pr " int %s = Int_val (%sv);\n" n n | Int64 n -> pr " int64_t %s = Int64_val (%sv);\n" n n + | Pointer (t, n) -> + pr " %s %s = (%s) (intptr_t) Int64_val (%sv);\n" t n t n ) args; (* Optional arguments. *) @@ -471,7 +474,7 @@ copy_table (char * const * argv) pr " free (%s);\n" n | StringList n | DeviceList n -> pr " ocaml_guestfs_free_strings (%s);\n" n; - | Bool _ | Int _ | Int64 _ -> () + | Bool _ | Int _ | Int64 _ | Pointer _ -> () ) args; List.iter ( function @@ -481,7 +484,7 @@ copy_table (char * const * argv) | Bool _ | Int _ | Int64 _ | Pathname _ | Device _ | Dev_or_Path _ | OptString _ | FileIn _ | FileOut _ | BufferIn _ | Key _ - | StringList _ | DeviceList _ -> () + | StringList _ | DeviceList _ | Pointer _ -> () ) optargs; pr " if (r == %s)\n" error_code; @@ -592,7 +595,7 @@ and generate_ocaml_function_type (ret, args, optargs) | StringList _ | DeviceList _ -> pr "string array -> " | Bool _ -> pr "bool -> " | Int _ -> pr "int -> " - | Int64 _ -> pr "int64 -> " + | Int64 _ | Pointer _ -> pr "int64 -> " ) args; (match ret with | RErr -> pr "unit" (* all errors are turned into exceptions *) diff --git a/generator/generator_perl.ml b/generator/generator_perl.ml index 96b8dd1..72f978d 100644 --- a/generator/generator_perl.ml +++ b/generator/generator_perl.ml @@ -242,6 +242,7 @@ clear_progress_callback (g) | Bool n -> pr " int %s;\n" n | Int n -> pr " int %s;\n" n | Int64 n -> pr " int64_t %s;\n" n + | Pointer (t, n) -> pr " %s %s;\n" t n ) args; (* PREINIT section (local variable declarations). *) @@ -362,7 +363,7 @@ clear_progress_callback (g) | Pathname _ | Device _ | Dev_or_Path _ | String _ | OptString _ | Bool _ | Int _ | Int64 _ | FileIn _ | FileOut _ - | BufferIn _ | Key _ -> () + | BufferIn _ | Key _ | Pointer _ -> () | StringList n | DeviceList n -> pr " free (%s);\n" n ) args; @@ -751,7 +752,7 @@ and generate_perl_prototype name (ret, args, optargs) match arg with | Pathname n | Device n | Dev_or_Path n | String n | OptString n | Bool n | Int n | Int64 n | FileIn n | FileOut n - | BufferIn n | Key n -> + | BufferIn n | Key n | Pointer (_, n) -> pr "$%s" n | StringList n | DeviceList n -> pr "\\@%s" n diff --git a/generator/generator_php.ml b/generator/generator_php.ml index b71d5c3..d405656 100644 --- a/generator/generator_php.ml +++ b/generator/generator_php.ml @@ -200,7 +200,7 @@ PHP_FUNCTION (guestfs_last_error) pr " char **%s;\n" n; | Bool n -> pr " zend_bool %s;\n" n - | Int n | Int64 n -> + | Int n | Int64 n | Pointer (_, n) -> pr " long %s;\n" n ) args; @@ -236,7 +236,7 @@ PHP_FUNCTION (guestfs_last_error) | OptString n -> "s!" | StringList n | DeviceList n -> "a" | Bool n -> "b" - | Int n | Int64 n -> "l" + | Int n | Int64 n | Pointer (_, n) -> "l" ) args ) in @@ -267,7 +267,7 @@ PHP_FUNCTION (guestfs_last_error) pr ", &z_%s" n | Bool n -> pr ", &%s" n - | Int n | Int64 n -> + | Int n | Int64 n | Pointer (_, n) -> pr ", &%s" n ) args; List.iter ( @@ -330,7 +330,7 @@ PHP_FUNCTION (guestfs_last_error) pr " %s[c] = NULL;\n" n; pr " }\n"; pr "\n" - | Bool n | Int n | Int64 n -> () + | Bool _ | Int _ | Int64 _ | Pointer _ -> () ) args; (* Optional arguments. *) @@ -406,7 +406,7 @@ PHP_FUNCTION (guestfs_last_error) pr " efree (%s);\n" n; pr " }\n"; pr "\n" - | Bool n | Int n | Int64 n -> () + | Bool _ | Int _ | Int64 _ | Pointer _ -> () ) args; (* Check for errors. *) diff --git a/generator/generator_python.ml b/generator/generator_python.ml index f85f5d6..bc570a8 100644 --- a/generator/generator_python.ml +++ b/generator/generator_python.ml @@ -324,6 +324,9 @@ py_guestfs_close (PyObject *self, PyObject *args) | Bool n -> pr " int %s;\n" n | Int n -> pr " int %s;\n" n | Int64 n -> pr " long long %s;\n" n + | Pointer (t, n) -> + pr " long long %s_int64;\n" n; + pr " %s %s;\n" t n ) args; if optargs <> [] then ( @@ -360,9 +363,11 @@ py_guestfs_close (PyObject *self, PyObject *args) | StringList _ | DeviceList _ -> pr "O" | Bool _ -> pr "i" (* XXX Python has booleans? *) | Int _ -> pr "i" - | Int64 _ -> pr "L" (* XXX Whoever thought it was a good idea to - * emulate C's int/long/long long in Python? - *) + | Int64 _ | Pointer _ -> + (* XXX Whoever thought it was a good idea to + * emulate C's int/long/long long in Python? + *) + pr "L" | BufferIn _ -> pr "s#" ) args; @@ -388,6 +393,7 @@ py_guestfs_close (PyObject *self, PyObject *args) | Bool n -> pr ", &%s" n | Int n -> pr ", &%s" n | Int64 n -> pr ", &%s" n + | Pointer (_, n) -> pr ", &%s_int64" n | BufferIn n -> pr ", &%s, &%s_size" n n ) args; @@ -409,6 +415,8 @@ py_guestfs_close (PyObject *self, PyObject *args) | StringList n | DeviceList n -> pr " %s = get_string_list (py_%s);\n" n n; pr " if (!%s) return NULL;\n" n + | Pointer (t, n) -> + pr " %s = (%s) (intptr_t) %s_int64;\n" n t n ) args; pr "\n"; @@ -444,7 +452,7 @@ py_guestfs_close (PyObject *self, PyObject *args) function | Pathname _ | Device _ | Dev_or_Path _ | String _ | Key _ | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ - | BufferIn _ -> () + | BufferIn _ | Pointer _ -> () | StringList n | DeviceList n -> pr " free (%s);\n" n ) args; diff --git a/generator/generator_ruby.ml b/generator/generator_ruby.ml index d0a7cd2..6d627b0 100644 --- a/generator/generator_ruby.ml +++ b/generator/generator_ruby.ml @@ -148,6 +148,8 @@ static VALUE ruby_guestfs_close (VALUE gv) pr " int %s = NUM2INT (%sv);\n" n n | Int64 n -> pr " long long %s = NUM2LL (%sv);\n" n n + | Pointer (t, n) -> + pr " %s %s = (%s) (intptr_t) NUM2LL (%sv);\n" t n t n ) args; pr "\n"; @@ -210,7 +212,7 @@ static VALUE ruby_guestfs_close (VALUE gv) function | Pathname _ | Device _ | Dev_or_Path _ | String _ | Key _ | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ | Int64 _ - | BufferIn _ -> () + | BufferIn _ | Pointer _ -> () | StringList n | DeviceList n -> pr " free (%s);\n" n ) args; diff --git a/generator/generator_types.ml b/generator/generator_types.ml index 262fb20..a480aac 100644 --- a/generator/generator_types.ml +++ b/generator/generator_types.ml @@ -185,6 +185,23 @@ and argt *) | FileIn of string | FileOut of string + (* This specifies an opaque pointer that is passed through + * untouched. Only non-daemon functions are supported. + * + * Pointer ("foo *", "bar") translates to "foo *bar" in the + * C API. The pointer ("bar") cannot be NULL. + * + * This is less well supported in other language bindings: + * if the pointer type is known then we may be able to produce + * a suitable binding, otherwise this is translated into a 64 + * bit int. + * + * Functions with this parameter type are not supported at all + * in guestfish (the function must be declared "NotInFish" else + * you will get an error). Also the function cannot contain + * tests, although we should fix this in future. + *) + | Pointer of (string * string) type flags | ProtocolLimitWarning (* display warning about protocol size limits *) diff --git a/generator/generator_utils.ml b/generator/generator_utils.ml index 425a579..6dc11bf 100644 --- a/generator/generator_utils.ml +++ b/generator/generator_utils.ml @@ -230,7 +230,7 @@ let map_chars f str let name_of_argt = function | Pathname n | Device n | Dev_or_Path n | String n | OptString n | StringList n | DeviceList n | Bool n | Int n | Int64 n - | FileIn n | FileOut n | BufferIn n | Key n -> n + | FileIn n | FileOut n | BufferIn n | Key n | Pointer (_, n) -> n let seq_of_test = function | TestRun s | TestOutput (s, _) | TestOutputList (s, _) diff --git a/generator/generator_xdr.ml b/generator/generator_xdr.ml index b44e2ef..c6d8a4d 100644 --- a/generator/generator_xdr.ml +++ b/generator/generator_xdr.ml @@ -86,6 +86,7 @@ let generate_xdr () | BufferIn n -> pr " opaque %s<>;\n" n | FileIn _ | FileOut _ -> () + | Pointer _ -> assert false ) args; pr "};\n\n" ); -- 1.7.3.2
Richard W.M. Jones
2010-Nov-10 11:46 UTC
[Libguestfs] [PATCH 5/7] New APIs: add-domain and add-libvirt-dom.
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://et.redhat.com/~rjones/virt-df/ -------------- next part -------------->From da5208bc5b08b6cd29c89ec84185755e80393241 Mon Sep 17 00:00:00 2001From: Richard Jones <rjones at redhat.com> Date: Tue, 9 Nov 2010 18:53:01 +0000 Subject: [PATCH 5/7] New APIs: add-domain and add-libvirt-dom. These two new APIs allow you to add the disks from a libvirt domain. The higher level add-domain API takes the name of the libvirt domain as a string and connects to libvirt itself. The lower level add-libvirt-dom API relies on the program to connect to libvirt and pass the virDomainPtr into the API call. In guestfish you can use the 'domain' command to access the higher level API, eg:><fs> domain Fedora14 libvirturi:qemu:///system1 The returned number is the number of disks that were added. --- TODO | 8 - generator/generator_actions.ml | 53 ++++++++ perl/typemap | 11 ++ po/POTFILES.in | 1 + regressions/Makefile.am | 1 + regressions/test-add-domain.sh | 79 +++++++++++ src/Makefile.am | 7 +- src/guestfs.h | 5 + src/virt.c | 287 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 443 insertions(+), 9 deletions(-) create mode 100755 regressions/test-add-domain.sh create mode 100644 src/virt.c diff --git a/TODO b/TODO index 0f933fa..2301c27 100644 --- a/TODO +++ b/TODO @@ -347,11 +347,3 @@ Eric Sandeen pointed out the blktrace tool which is a better way of capturing traces than using patched qemu (see contrib/visualize-alignment). We would still use the same visualization tools in conjunction with blktrace traces. - -Add-domain command ------------------- - -guestfs_add_domain (g, "libvirt-dom"); - -However this would need to not depend on libvirt, eg. loading it -on demand. diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml index a0a337b..6b77a00 100644 --- a/generator/generator_actions.ml +++ b/generator/generator_actions.ml @@ -1058,6 +1058,59 @@ Please read L<guestfs(3)/INSPECTION> for more details."); This returns the internal QEMU command line. 'debug' commands are not part of the formal API and can be removed or changed at any time."); + ("add_libvirt_dom", (RInt "nrdisks", [Pointer ("virDomainPtr", "dom")], [Bool "readonly"; String "iface"]), -1, [NotInFish], + [], + "add the disk(s) from a libvirt domain", + "\ +This function adds the disk(s) attached to the libvirt domain C<dom>. +It works by requesting the domain XML from libvirt, parsing it for +disks, and calling C<guestfs_add_drive_opts> on each one. + +The number of disks added is returned. This operation is atomic: +if an error is returned, then no disks are added. + +If C<readonly> is true then you can add any libvirt domain. Otherwise +this function checks that the domain is shut off. + +Disks must be accessible locally. This often means that adding disks +from a remote libvirt connection (see L<http://libvirt.org/remote.html>) +will fail unless those disks are accessible via the same device path +locally too. + +The optional parameters are passed directly through to +C<guestfs_add_drive_opts>."); + + ("add_domain", (RInt "nrdisks", [String "dom"], [String "libvirturi"; Bool "readonly"; String "iface"]), -1, [FishAlias "domain"], + [], + "add the disk(s) from a named libvirt domain", + "\ +This function adds the disk(s) attached to the named libvirt +domain C<dom>. It works by connecting to libvirt, requesting +the domain and domain XML from libvirt, parsing it for disks, +and calling C<guestfs_add_drive_opts> on each one. + +The number of disks added is returned. This operation is atomic: +if an error is returned, then no disks are added. + +If C<readonly> is true then you can add any libvirt domain. Otherwise +this function checks that the domain is shut off. + +Disks must be accessible locally. This often means that adding disks +from a remote libvirt connection (see L<http://libvirt.org/remote.html>) +will fail unless those disks are accessible via the same device path +locally too. + +The optional C<libvirturi> parameter sets the libvirt URI +(see L<http://libvirt.org/uri.html>). If this is not set then +we connect to the default libvirt URI (or one set through an +environment variable, see the libvirt documentation for full +details). If you are using the C API directly then it is more +flexible to create the libvirt connection object yourself, get +the domain object, and call C<guestfs_add_libvirt_dom>. + +The other optional parameters are passed directly through to +C<guestfs_add_drive_opts>."); + ] (* daemon_functions are any functions which cause some action diff --git a/perl/typemap b/perl/typemap index d978e60..223aee1 100644 --- a/perl/typemap +++ b/perl/typemap @@ -3,6 +3,7 @@ char * T_PV const char * T_PV guestfs_h * O_OBJECT_guestfs_h int64_t T_IV +virDomainPtr O_OBJECT_domain INPUT O_OBJECT_guestfs_h @@ -18,6 +19,16 @@ O_OBJECT_guestfs_h croak (\"${Package}::$func_name(): $var is not a blessed HV reference\"); } +# This comes from the Sys::Virt bindings. +INPUT +O_OBJECT_domain + if (sv_isobject ($arg) && (SvTYPE (SvRV ($arg)) == SVt_PVMG)) + $var = ($type)SvIV ((SV*) SvRV ($arg)); + else { + warn(\"${Package}::$func_name() -- $var is not a blessed SV reference\"); + XSRETURN_UNDEF; + } + OUTPUT O_OBJECT_guestfs_h sv_setiv ($arg, PTR2IV ($var)); diff --git a/po/POTFILES.in b/po/POTFILES.in index e8d9587..d76904d 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -131,6 +131,7 @@ src/inspect.c src/launch.c src/listfs.c src/proto.c +src/virt.c test-tool/helper.c test-tool/test-tool.c tools/virt-cat.pl diff --git a/regressions/Makefile.am b/regressions/Makefile.am index c9156f9..10c5c48 100644 --- a/regressions/Makefile.am +++ b/regressions/Makefile.am @@ -32,6 +32,7 @@ TESTS = \ rhbz576879.sh \ rhbz578407.sh \ rhbz580246.sh \ + test-add-domain.sh \ test-cancellation-download-librarycancels.sh \ test-cancellation-upload-daemoncancels.sh \ test-copy.sh \ diff --git a/regressions/test-add-domain.sh b/regressions/test-add-domain.sh new file mode 100755 index 0000000..d124b62 --- /dev/null +++ b/regressions/test-add-domain.sh @@ -0,0 +1,79 @@ +#!/bin/bash - +# libguestfs +# 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. + +# Test add-domain command. + +set -e + +rm -f test1.img test2.img test3.img test.xml test.out + +cwd="$(pwd)" + +truncate -s 1M test1.img test2.img test3.img + +# Libvirt test XML, see libvirt.git/examples/xml/test/testnode.xml +cat > test.xml <<EOF +<node> + <domain type="test"> + <name>guest</name> + <os> + <type>hvm</type> + <boot dev='hd'/> + </os> + <memory>524288</memory> + <devices> + <disk type="file"> + <source file="$cwd/test1.img"/> + <target dev="hda"/> + </disk> + <disk type="file"> + <driver name="qemu" type="raw"/> + <source file="$cwd/test2.img"/> + <target dev="hdb"/> + </disk> + <disk type="file"> + <driver name="qemu" type="qcow2"/> + <source file="$cwd/test3.img"/> + <target dev="hdc"/> + </disk> + </devices> + </domain> +</node> +EOF + +../fish/guestfish >test.out <<EOF + domain guest libvirturi:test://$cwd/test.xml readonly:true + debug-cmdline +EOF +grep -sq "test1.img.*snapshot=on" test.out +! grep -sq "test1.img.*format" test.out +grep -sq "test2.img.*snapshot=on.*format=raw" test.out +grep -sq "test3.img.*snapshot=on.*format=qcow2" test.out + +# Test atomicity. +rm test3.img + +../fish/guestfish >test.out <<EOF + -domain guest libvirturi:test://$cwd/test.xml readonly:true + debug-cmdline +EOF +! grep -sq "test1.img" test.out +! grep -sq "test2.img" test.out +! grep -sq "test3.img" test.out + +rm -f test1.img test2.img test3.img test.xml test.out diff --git a/src/Makefile.am b/src/Makefile.am index 4be61a5..d8a7f55 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -133,9 +133,13 @@ libguestfs_la_SOURCES = \ launch.c \ listfs.c \ proto.c \ + virt.c \ libguestfs.syms -libguestfs_la_LIBADD = $(HIVEX_LIBS) $(AUGEAS_LIBS) $(PCRE_LIBS) $(MAGIC_LIBS) $(LTLIBTHREAD) ../gnulib/lib/libgnu.la +libguestfs_la_LIBADD = \ + $(HIVEX_LIBS) $(AUGEAS_LIBS) $(PCRE_LIBS) $(MAGIC_LIBS) \ + $(LIBVIRT_LIBS) $(LIBXML2_LIBS) \ + $(LTLIBTHREAD) ../gnulib/lib/libgnu.la # Make libguestfs include the convenience libraries. noinst_LTLIBRARIES = liberrnostring.la libprotocol.la @@ -144,6 +148,7 @@ libguestfs_la_LIBADD += liberrnostring.la libprotocol.la libguestfs_la_CFLAGS = \ -DGUESTFS_DEFAULT_PATH='"$(libdir)/guestfs"' \ $(HIVEX_CFLAGS) $(AUGEAS_CFLAGS) $(PCRE_CFLAGS) \ + $(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \ $(WARN_CFLAGS) $(WERROR_CFLAGS) libguestfs_la_CPPFLAGS = -I$(top_srcdir)/gnulib/lib diff --git a/src/guestfs.h b/src/guestfs.h index 0f4f9fd..be7398a 100644 --- a/src/guestfs.h +++ b/src/guestfs.h @@ -79,6 +79,11 @@ extern void guestfs_set_private (guestfs_h *g, const char *key, void *data); extern void *guestfs_get_private (guestfs_h *g, const char *key); /*--- Structures and actions ---*/ + +/* Hack to avoid needing to include <libvirt.h> */ +typedef struct _virDomain virDomain; +typedef virDomain *virDomainPtr; + #include <rpc/types.h> #include <rpc/xdr.h> #include <guestfs-structs.h> diff --git a/src/virt.c b/src/virt.c new file mode 100644 index 0000000..5816f61 --- /dev/null +++ b/src/virt.c @@ -0,0 +1,287 @@ +/* libguestfs + * Copyright (C) 2010 Red Hat Inc. + * + * 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 + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#ifdef HAVE_LIBVIRT +#include <libvirt/libvirt.h> +#include <libvirt/virterror.h> +#endif + +#ifdef HAVE_LIBXML2 +#include <libxml/xpath.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#endif + +#include "guestfs.h" +#include "guestfs-internal.h" +#include "guestfs-internal-actions.h" +#include "guestfs_protocol.h" + +#if defined(HAVE_LIBVIRT) && defined(HAVE_LIBXML2) + +static void init_libxml2 (void) __attribute__((constructor)); + +static void +init_libxml2 (void) +{ + /* I am told that you don't really need to call virInitialize ... */ + + xmlInitParser (); + LIBXML_TEST_VERSION; +} + +int +guestfs__add_domain (guestfs_h *g, const char *domain_name, + const struct guestfs_add_domain_argv *optargs) +{ + virErrorPtr err; + virConnectPtr conn = NULL; + virDomainPtr dom = NULL; + int r = -1; + const char *libvirturi; + int readonly; + const char *iface; + struct guestfs_add_libvirt_dom_argv optargs2 = { .bitmask = 0 }; + + libvirturi = optargs->bitmask & GUESTFS_ADD_DOMAIN_LIBVIRTURI_BITMASK + ? optargs->libvirturi : NULL; + readonly = optargs->bitmask & GUESTFS_ADD_DOMAIN_READONLY_BITMASK + ? optargs->readonly : 0; + iface = optargs->bitmask & GUESTFS_ADD_DOMAIN_IFACE_BITMASK + ? optargs->iface : NULL; + + /* Connect to libvirt, find the domain. */ + conn = virConnectOpenReadOnly (libvirturi); + if (!conn) { + err = virGetLastError (); + error (g, _("could not connect to libvirt (code %d, domain %d): %s"), + err->code, err->domain, err->message); + goto cleanup; + } + + dom = virDomainLookupByName (conn, domain_name); + if (!dom) { + err = virConnGetLastError (conn); + error (g, _("no libvirt domain called '%s': %s"), + domain_name, err->message); + goto cleanup; + } + + if (readonly) { + optargs2.bitmask |= GUESTFS_ADD_LIBVIRT_DOM_READONLY_BITMASK; + optargs2.readonly = readonly; + } + if (iface) { + optargs2.bitmask |= GUESTFS_ADD_LIBVIRT_DOM_IFACE_BITMASK; + optargs2.iface = iface; + } + + r = guestfs__add_libvirt_dom (g, dom, &optargs2); + + cleanup: + if (dom) virDomainFree (dom); + if (conn) virConnectClose (conn); + + return r; +} + +int +guestfs__add_libvirt_dom (guestfs_h *g, virDomainPtr dom, + const struct guestfs_add_libvirt_dom_argv *optargs) +{ + int r = -1, nr_added = 0, i; + virErrorPtr err; + virConnectPtr conn = virDomainGetConnect (dom); + xmlDocPtr doc = NULL; + xmlXPathContextPtr xpathCtx = NULL; + xmlXPathObjectPtr xpathObj = NULL; + char *xml = NULL; + int readonly; + const char *iface; + int cmdline_pos; + + cmdline_pos = guestfs___checkpoint_cmdline (g); + + readonly = optargs->bitmask & GUESTFS_ADD_LIBVIRT_DOM_READONLY_BITMASK + ? optargs->readonly : 0; + iface = optargs->bitmask & GUESTFS_ADD_LIBVIRT_DOM_IFACE_BITMASK + ? optargs->iface : NULL; + + if (!readonly) { + virDomainInfo info; + if (virDomainGetInfo (dom, &info) == -1) { + err = virConnGetLastError (conn); + error (g, _("error getting domain info: %s"), err->message); + goto cleanup; + } + if (info.state != VIR_DOMAIN_SHUTOFF) { + error (g, _("error: domain is a live virtual machine.\nYou must use readonly access because write access to a running virtual machine\ncan cause disk corruption.")); + goto cleanup; + } + } + + /* Domain XML. */ + xml = virDomainGetXMLDesc (dom, 0); + + if (!xml) { + err = virConnGetLastError (conn); + error (g, _("error reading libvirt XML information: %s"), + err->message); + goto cleanup; + } + + /* Now the horrible task of parsing out the fields we need from the XML. + * http://www.xmlsoft.org/examples/xpath1.c + */ + doc = xmlParseMemory (xml, strlen (xml)); + if (doc == NULL) { + error (g, _("unable to parse XML information returned by libvirt")); + goto cleanup; + } + + xpathCtx = xmlXPathNewContext (doc); + if (xpathCtx == NULL) { + error (g, _("unable to create new XPath context")); + goto cleanup; + } + + /* This gives us a set of all the <disk> nodes. */ + xpathObj = xmlXPathEvalExpression (BAD_CAST "//devices/disk", xpathCtx); + if (xpathObj == NULL) { + error (g, _("unable to evaluate XPath expression")); + goto cleanup; + } + + xmlNodeSetPtr nodes = xpathObj->nodesetval; + for (i = 0; i < nodes->nodeNr; ++i) { + xmlXPathObjectPtr xpfilename; + xmlXPathObjectPtr xpformat; + + /* Change the context to the current <disk> node. + * DV advises to reset this before each search since older versions of + * libxml2 might overwrite it. + */ + xpathCtx->node = nodes->nodeTab[i]; + + /* Filename can be in <source dev=..> or <source file=..> attribute. */ + xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@dev", xpathCtx); + if (xpfilename == NULL || + xpfilename->nodesetval == NULL || + xpfilename->nodesetval->nodeNr == 0) { + xmlXPathFreeObject (xpfilename); + xpathCtx->node = nodes->nodeTab[i]; + xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@file", xpathCtx); + if (xpfilename == NULL || + xpfilename->nodesetval == NULL || + xpfilename->nodesetval->nodeNr == 0) { + xmlXPathFreeObject (xpfilename); + continue; /* disk filename not found, skip this */ + } + } + + assert (xpfilename->nodesetval->nodeTab[0]); + assert (xpfilename->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE); + xmlAttrPtr attr = (xmlAttrPtr) xpfilename->nodesetval->nodeTab[0]; + char *filename = (char *) xmlNodeListGetString (doc, attr->children, 1); + + /* Get the disk format (may not be set). */ + xpathCtx->node = nodes->nodeTab[i]; + xpformat = xmlXPathEvalExpression (BAD_CAST "./driver/@type", xpathCtx); + char *format = NULL; + if (xpformat != NULL && + xpformat->nodesetval && + xpformat->nodesetval->nodeNr > 0) { + assert (xpformat->nodesetval->nodeTab[0]); + assert (xpformat->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE); + attr = (xmlAttrPtr) xpformat->nodesetval->nodeTab[0]; + format = (char *) xmlNodeListGetString (doc, attr->children, 1); + } + + /* Add the disk, with optional format. */ + struct guestfs_add_drive_opts_argv optargs2 = { .bitmask = 0 }; + if (readonly) { + optargs2.bitmask |= GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK; + optargs2.readonly = readonly; + } + if (format) { + optargs2.bitmask |= GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK; + optargs2.format = format; + } + if (iface) { + optargs2.bitmask |= GUESTFS_ADD_DRIVE_OPTS_IFACE_BITMASK; + optargs2.iface = iface; + } + + int t = guestfs__add_drive_opts (g, filename, &optargs2); + + xmlFree (filename); + xmlFree (format); + xmlXPathFreeObject (xpfilename); + xmlXPathFreeObject (xpformat); + + if (t == -1) + goto cleanup; + + nr_added++; + } + + if (nr_added == 0) { + error (g, _("libvirt domain has no disks")); + goto cleanup; + } + + /* Successful. */ + r = nr_added; + + cleanup: + if (r == -1) guestfs___rollback_cmdline (g, cmdline_pos); + free (xml); + if (xpathObj) xmlXPathFreeObject (xpathObj); + if (xpathCtx) xmlXPathFreeContext (xpathCtx); + if (doc) xmlFreeDoc (doc); + + return r; +} + +#else /* no libvirt or libxml2 at compile time */ + +#define NOT_IMPL(r) \ + error (g, _("add-domain APIs not available since this version of libguestfs was compiled without libvirt or libxml2")); \ + return r + +int +guestfs__add_domain (guestfs_h *g, const char *dom, + const struct guestfs_add_domain_argv *optargs) +{ + NOT_IMPL(-1); +} + +int +guestfs__add_libvirt_dom (guestfs_h *g, virDomainPtr dom, + const struct guestfs_add_libvirt_dom_argv *optargs) +{ + NOT_IMPL(-1); +} + +#endif /* no libvirt or libxml2 at compile time */ -- 1.7.3.2
Richard W.M. Jones
2010-Nov-10 11:46 UTC
[Libguestfs] [PATCH 6/7] fish: Use core add-domain API to implement '-d' option.
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming blog: http://rwmj.wordpress.com Fedora now supports 80 OCaml packages (the OPEN alternative to F#) http://cocan.org/getting_started_with_ocaml_on_red_hat_and_fedora -------------- next part -------------->From 40be7aad9f9a775d409e72185c6a18267e11d632 Mon Sep 17 00:00:00 2001From: Richard Jones <rjones at redhat.com> Date: Tue, 9 Nov 2010 18:56:00 +0000 Subject: [PATCH 6/7] fish: Use core add-domain API to implement '-d' option. This also makes libxml2 and libvirt into optional dependencies. If they are missing then the core API will print an error, as will the '-d' option to guestfish. --- README | 4 +- configure.ac | 23 +++-- fish/Makefile.am | 2 - fish/virt.c | 189 ++------------------------------------- fuse/Makefile.am | 3 +- regressions/test-guestfish-d.sh | 12 ++-- 6 files changed, 31 insertions(+), 202 deletions(-) diff --git a/README b/README index 8b88b98..65e22cb 100644 --- a/README +++ b/README @@ -51,9 +51,9 @@ Requirements - libmagic (the library that corresponds to the 'file' command) (optional) -- libvirt +- libvirt (optional) -- libxml2 +- libxml2 (optional) - Augeas (http://augeas.net/) (optional) diff --git a/configure.ac b/configure.ac index e7761c9..67b1309 100644 --- a/configure.ac +++ b/configure.ac @@ -445,11 +445,6 @@ AC_CHECK_LIB([magic],[magic_file], ], [AC_MSG_WARN([libmagic not found, some core features will be disabled])]) -dnl libvirt (required) -PKG_CHECK_MODULES([LIBVIRT], [libvirt]) -AC_SUBST([LIBVIRT_CFLAGS]) -AC_SUBST([LIBVIRT_LIBS]) - dnl Check for PCRE (highly recommended) PKG_CHECK_MODULES([PCRE], [libpcre], [AC_SUBST([PCRE_CFLAGS]) @@ -458,11 +453,21 @@ PKG_CHECK_MODULES([PCRE], [libpcre], ], [AC_MSG_WARN([PCRE not found, some core features will be disabled])]) -dnl libxml2 (required) -PKG_CHECK_MODULES([LIBXML2], [libxml-2.0]) -AC_SUBST([LIBXML2_CFLAGS]) -AC_SUBST([LIBXML2_LIBS]) +dnl libvirt (highly recommended) +PKG_CHECK_MODULES([LIBVIRT], [libvirt], + [AC_SUBST([LIBVIRT_CFLAGS]) + AC_SUBST([LIBVIRT_LIBS]) + AC_DEFINE([HAVE_LIBVIRT],[1],[libvirt found at compile time.]) + ], + [AC_MSG_WARN([libvirt not found, some core features will be disabled])]) +dnl libxml2 (highly recommended) +PKG_CHECK_MODULES([LIBXML2], [libxml-2.0], + [AC_SUBST([LIBXML2_CFLAGS]) + AC_SUBST([LIBXML2_LIBS]) + AC_DEFINE([HAVE_LIBXML2],[1],[libxml2 found at compile time.]) + ], + [AC_MSG_WARN([libxml2 not found, some core features will be disabled])]) dnl hivex library (highly recommended) dnl This used to be a part of libguestfs, but was spun off into its diff --git a/fish/Makefile.am b/fish/Makefile.am index dadda91..6debdce 100644 --- a/fish/Makefile.am +++ b/fish/Makefile.am @@ -103,11 +103,9 @@ guestfish_CFLAGS = \ -DGUESTFS_DEFAULT_PATH='"$(libdir)/guestfs"' \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(srcdir)/../gnulib/lib -I../gnulib/lib \ - $(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \ $(WARN_CFLAGS) $(WERROR_CFLAGS) guestfish_LDADD = \ - $(LIBVIRT_LIBS) $(LIBXML2_LIBS) \ $(top_builddir)/src/libguestfs.la $(LIBREADLINE) -lm # Make guestfish use the convenience libraries. diff --git a/fish/virt.c b/fish/virt.c index 728f9c2..13a6d12 100644 --- a/fish/virt.c +++ b/fish/virt.c @@ -23,199 +23,26 @@ #include <string.h> #include <assert.h> -#include <libvirt/libvirt.h> -#include <libvirt/virterror.h> - -#include <libxml/xpath.h> -#include <libxml/parser.h> -#include <libxml/tree.h> - #include "guestfs.h" #include "options.h" /* Implements the guts of the '-d' option. - * - * Note that we have to observe the '--ro' flag in two respects: by - * adding the drives read-only if the flag is set, and by restricting - * guests to shut down ones unless '--ro' is set. - * * Returns the number of drives added (> 0), or -1 for failure. */ int add_libvirt_drives (const char *guest) { - static int initialized = 0; - if (!initialized) { - initialized = 1; - - if (virInitialize () == -1) - return -1; - - xmlInitParser (); - LIBXML_TEST_VERSION; - } - - int r = -1, nr_added = 0, i; - virErrorPtr err; - virConnectPtr conn = NULL; - virDomainPtr dom = NULL; - xmlDocPtr doc = NULL; - xmlXPathContextPtr xpathCtx = NULL; - xmlXPathObjectPtr xpathObj = NULL; - char *xml = NULL; - - /* Connect to libvirt, find the domain. */ - conn = virConnectOpenReadOnly (libvirt_uri); - if (!conn) { - err = virGetLastError (); - fprintf (stderr, _("%s: could not connect to libvirt (code %d, domain %d): %s\n"), - program_name, err->code, err->domain, err->message); - goto cleanup; - } - - dom = virDomainLookupByName (conn, guest); - if (!dom) { - err = virConnGetLastError (conn); - fprintf (stderr, _("%s: no libvirt domain called '%s': %s\n"), - program_name, guest, err->message); - goto cleanup; - } - if (!read_only) { - virDomainInfo info; - if (virDomainGetInfo (dom, &info) == -1) { - err = virConnGetLastError (conn); - fprintf (stderr, _("%s: error getting domain info about '%s': %s\n"), - program_name, guest, err->message); - goto cleanup; - } - if (info.state != VIR_DOMAIN_SHUTOFF) { - fprintf (stderr, _("%s: error: '%s' is a live virtual machine.\nYou must use '--ro' because write access to a running virtual machine can\ncause disk corruption.\n"), - program_name, guest); - goto cleanup; - } - } - - /* Domain XML. */ - xml = virDomainGetXMLDesc (dom, 0); - - if (!xml) { - err = virConnGetLastError (conn); - fprintf (stderr, _("%s: error reading libvirt XML information about '%s': %s\n"), - program_name, guest, err->message); - goto cleanup; - } - - /* Now the horrible task of parsing out the fields we need from the XML. - * http://www.xmlsoft.org/examples/xpath1.c - */ - doc = xmlParseMemory (xml, strlen (xml)); - if (doc == NULL) { - fprintf (stderr, _("%s: unable to parse XML information returned by libvirt\n"), - program_name); - goto cleanup; - } + struct guestfs_add_domain_argv optargs = { .bitmask = 0 }; - xpathCtx = xmlXPathNewContext (doc); - if (xpathCtx == NULL) { - fprintf (stderr, _("%s: unable to create new XPath context\n"), - program_name); - goto cleanup; + if (libvirt_uri) { + optargs.bitmask |= GUESTFS_ADD_DOMAIN_LIBVIRTURI_BITMASK; + optargs.libvirturi = libvirt_uri; } - - /* This gives us a set of all the <disk> nodes. */ - xpathObj = xmlXPathEvalExpression (BAD_CAST "//devices/disk", xpathCtx); - if (xpathObj == NULL) { - fprintf (stderr, _("%s: unable to evaluate XPath expression\n"), - program_name); - goto cleanup; + if (read_only) { + optargs.bitmask |= GUESTFS_ADD_DOMAIN_READONLY_BITMASK; + optargs.readonly = 1; } - xmlNodeSetPtr nodes = xpathObj->nodesetval; - for (i = 0; i < nodes->nodeNr; ++i) { - xmlXPathObjectPtr xpfilename; - xmlXPathObjectPtr xpformat; - - /* Change the context to the current <disk> node. - * DV advises to reset this before each search since older versions of - * libxml2 might overwrite it. - */ - xpathCtx->node = nodes->nodeTab[i]; - - /* Filename can be in <source dev=..> or <source file=..> attribute. */ - xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@dev", xpathCtx); - if (xpfilename == NULL || - xpfilename->nodesetval == NULL || - xpfilename->nodesetval->nodeNr == 0) { - xmlXPathFreeObject (xpfilename); - xpathCtx->node = nodes->nodeTab[i]; - xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@file", xpathCtx); - if (xpfilename == NULL || - xpfilename->nodesetval == NULL || - xpfilename->nodesetval->nodeNr == 0) { - xmlXPathFreeObject (xpfilename); - continue; /* disk filename not found, skip this */ - } - } - - assert (xpfilename->nodesetval->nodeTab[0]); - assert (xpfilename->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE); - xmlAttrPtr attr = (xmlAttrPtr) xpfilename->nodesetval->nodeTab[0]; - char *filename = (char *) xmlNodeListGetString (doc, attr->children, 1); - - /* Get the disk format (may not be set). */ - xpathCtx->node = nodes->nodeTab[i]; - xpformat = xmlXPathEvalExpression (BAD_CAST "./driver/@type", xpathCtx); - char *format = NULL; - if (xpformat != NULL && - xpformat->nodesetval && - xpformat->nodesetval->nodeNr > 0) { - assert (xpformat->nodesetval->nodeTab[0]); - assert (xpformat->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE); - attr = (xmlAttrPtr) xpformat->nodesetval->nodeTab[0]; - format = (char *) xmlNodeListGetString (doc, attr->children, 1); - } - - /* Add the disk, with optional format. */ - struct guestfs_add_drive_opts_argv optargs = { .bitmask = 0 }; - if (read_only) { - optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK; - optargs.readonly = read_only; - } - if (format) { - optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK; - optargs.format = format; - } - - int t = guestfs_add_drive_opts_argv (g, filename, &optargs); - - xmlFree (filename); - xmlFree (format); - xmlXPathFreeObject (xpfilename); - xmlXPathFreeObject (xpformat); - - if (t == -1) - goto cleanup; - - nr_added++; - } - - if (nr_added == 0) { - fprintf (stderr, _("%s: libvirt domain '%s' has no disks\n"), - program_name, guest); - goto cleanup; - } - - /* Successful. */ - r = nr_added; - -cleanup: - free (xml); - if (xpathObj) xmlXPathFreeObject (xpathObj); - if (xpathCtx) xmlXPathFreeContext (xpathCtx); - if (doc) xmlFreeDoc (doc); - if (dom) virDomainFree (dom); - if (conn) virConnectClose (conn); - - return r; + return guestfs_add_domain_argv (g, guest, &optargs); } diff --git a/fuse/Makefile.am b/fuse/Makefile.am index ab63584..0a1d9da 100644 --- a/fuse/Makefile.am +++ b/fuse/Makefile.am @@ -45,12 +45,11 @@ guestmount_CFLAGS = \ -I$(srcdir)/../gnulib/lib -I../gnulib/lib \ -DGUESTFS_DEFAULT_PATH='"$(libdir)/guestfs"' \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ - $(FUSE_CFLAGS) $(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \ + $(FUSE_CFLAGS) \ $(WARN_CFLAGS) $(WERROR_CFLAGS) guestmount_LDADD = \ $(FUSE_LIBS) -lulockmgr \ - $(LIBVIRT_LIBS) $(LIBXML2_LIBS) \ $(top_builddir)/src/libguestfs.la \ ../gnulib/lib/libgnu.la diff --git a/regressions/test-guestfish-d.sh b/regressions/test-guestfish-d.sh index be20748..7fc5251 100755 --- a/regressions/test-guestfish-d.sh +++ b/regressions/test-guestfish-d.sh @@ -56,11 +56,11 @@ cat > test.xml <<EOF </node> EOF -../fish/guestfish -c "test://$cwd/test.xml" --ro -d guest -x \ - </dev/null >test.out 2>&1 -grep -sq '^add_drive.*test1.img.*readonly:true' test.out -! grep -sq '^add_drive.*test1.img.*format' test.out -grep -sq '^add_drive.*test2.img.*readonly:true.*format:raw' test.out -grep -sq '^add_drive.*test3.img.*readonly:true.*format:qcow2' test.out +../fish/guestfish -c "test://$cwd/test.xml" --ro -d guest \ + debug-cmdline </dev/null >test.out +grep -sq "test1.img.*snapshot=on" test.out +! grep -sq "test1.img.*format" test.out +grep -sq "test2.img.*snapshot=on.*format=raw" test.out +grep -sq "test3.img.*snapshot=on.*format=qcow2" test.out rm -f test1.img test2.img test3.img test.xml test.out -- 1.7.3.2
Richard W.M. Jones
2010-Nov-10 11:47 UTC
[Libguestfs] [PATCH 7/7] capitests: Test add-libvirt-dom C API.
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones New in Fedora 11: Fedora Windows cross-compiler. Compile Windows programs, test, and build Windows installers. Over 70 libraries supprt'd http://fedoraproject.org/wiki/MinGW http://www.annexia.org/fedora_mingw -------------- next part -------------->From 42f9a73ab0bf5af74853e86a04898a6a1cebd6d8 Mon Sep 17 00:00:00 2001From: Richard W.M. Jones <rjones at redhat.com> Date: Wed, 10 Nov 2010 11:31:01 +0000 Subject: [PATCH 7/7] capitests: Test add-libvirt-dom C API. --- .gitignore | 1 + capitests/Makefile.am | 16 ++++ capitests/test-add-libvirt-dom.c | 151 ++++++++++++++++++++++++++++++++++++++ configure.ac | 1 + 4 files changed, 169 insertions(+), 0 deletions(-) create mode 100644 capitests/test-add-libvirt-dom.c diff --git a/.gitignore b/.gitignore index d42898a..bf49d56 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ autom4te.cache *.bak bindtests.tmp capitests/test-add-drive-opts +capitests/test-add-libvirt-dom capitests/test-command capitests/test-config capitests/test-create-handle diff --git a/capitests/Makefile.am b/capitests/Makefile.am index 6577f69..9a3455e 100644 --- a/capitests/Makefile.am +++ b/capitests/Makefile.am @@ -38,6 +38,11 @@ TESTS = \ test-add-drive-opts \ test-last-errno +if HAVE_LIBVIRT +check_PROGRAMS += test-add-libvirt-dom +TESTS += test-add-libvirt-dom +endif + TESTS_ENVIRONMENT = \ SKIP_TEST_COMMAND=$(shell ldd test-command | grep -sq 'not a dynamic executable' || echo 1) \ SKIP_TEST_COMMAND_LINES=$(shell ldd test-command | grep -sq 'not a dynamic executable' || echo 1) \ @@ -86,6 +91,17 @@ test_last_errno_CFLAGS = \ test_last_errno_LDADD = \ $(top_builddir)/src/libguestfs.la +if HAVE_LIBVIRT +test_add_libvirt_dom_SOURCES = test-add-libvirt-dom.c +test_add_libvirt_dom_CFLAGS = \ + -I$(top_srcdir)/src -I$(top_builddir)/src -I$(top_srcdir)/gnulib/lib \ + $(LIBVIRT_CFLAGS) \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) +test_add_libvirt_dom_LDADD = \ + $(top_builddir)/src/libguestfs.la $(LIBVIRT_LIBS) \ + $(LTLIBTHREAD) ../gnulib/lib/libgnu.la +endif + # Run the tests under valgrind. valgrind: diff --git a/capitests/test-add-libvirt-dom.c b/capitests/test-add-libvirt-dom.c new file mode 100644 index 0000000..7861069 --- /dev/null +++ b/capitests/test-add-libvirt-dom.c @@ -0,0 +1,151 @@ +/* libguestfs + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "xgetcwd.h" + +#include <libvirt/libvirt.h> +#include <libvirt/virterror.h> + +#include "guestfs.h" + +static void +make_test_xml (FILE *fp, const char *cwd) +{ + fprintf (fp, + "<?xml version=\"1.0\"?>\n" + "<node>\n" + " <domain type='test'>\n" + " <name>guest</name>\n" + " <os>\n" + " <type>hvm</type>\n" + " <boot dev='hd'/>\n" + " </os>\n" + " <memory>524288</memory>\n" + " <devices>\n" + " <disk type='file'>\n" + " <source file='%s/test1.img'/>\n" + " <target dev='hda'/>\n" + " </disk>\n" + " <disk type='file'>\n" + " <driver name='qemu' type='raw'/>\n" + " <source file='%s/test2.img'/>\n" + " <target dev='hdb'/>\n" + " </disk>\n" + " <disk type='file'>\n" + " <driver name='qemu' type='qcow2'/>\n" + " <source file='%s/test3.img'/>\n" + " <target dev='hdc'/>\n" + " </disk>\n" + " </devices>\n" + " </domain>\n" + "</node>", + cwd, cwd, cwd); +} + +int +main (int argc, char *argv[]) +{ + guestfs_h *g; + virConnectPtr conn; + virDomainPtr dom; + virErrorPtr err; + int r; + const char *test_xml; + char *cwd; + FILE *fp; + char libvirt_uri[1024]; + + cwd = xgetcwd (); + + /* Create the libvirt XML and test images in the current directory. */ + fp = fopen ("test.xml", "w"); + if (fp == NULL) { + perror ("test.xml"); + exit (EXIT_FAILURE); + } + make_test_xml (fp, cwd); + fclose (fp); + + fp = fopen ("test1.img", "w"); + if (fp == NULL) { + perror ("test1.img"); + exit (EXIT_FAILURE); + } + fclose (fp); + + fp = fopen ("test2.img", "w"); + if (fp == NULL) { + perror ("test2.img"); + exit (EXIT_FAILURE); + } + fclose (fp); + + fp = fopen ("test3.img", "w"); + if (fp == NULL) { + perror ("test3.img"); + exit (EXIT_FAILURE); + } + fclose (fp); + + /* Create the guestfs handle. */ + g = guestfs_create (); + if (g == NULL) { + fprintf (stderr, "failed to create handle\n"); + exit (EXIT_FAILURE); + } + + /* Create the libvirt connection. */ + snprintf (libvirt_uri, sizeof libvirt_uri, "test://%s/test.xml", cwd); + conn = virConnectOpenReadOnly (libvirt_uri); + if (!conn) { + err = virGetLastError (); + fprintf (stderr, "could not connect to libvirt (code %d, domain %d): %s\n", + err->code, err->domain, err->message); + exit (EXIT_FAILURE); + } + + dom = virDomainLookupByName (conn, "guest"); + if (!dom) { + err = virConnGetLastError (conn); + fprintf (stderr, + "no libvirt domain called '%s': %s\n", "guest", err->message); + exit (EXIT_FAILURE); + } + + r = guestfs_add_libvirt_dom (g, dom, + GUESTFS_ADD_LIBVIRT_DOM_READONLY, 1, + -1); + if (r == -1) + exit (EXIT_FAILURE); + + guestfs_close (g); + + unlink ("test.xml"); + unlink ("test1.img"); + unlink ("test2.img"); + unlink ("test3.img"); + + exit (EXIT_SUCCESS); +} diff --git a/configure.ac b/configure.ac index 67b1309..2594369 100644 --- a/configure.ac +++ b/configure.ac @@ -460,6 +460,7 @@ PKG_CHECK_MODULES([LIBVIRT], [libvirt], AC_DEFINE([HAVE_LIBVIRT],[1],[libvirt found at compile time.]) ], [AC_MSG_WARN([libvirt not found, some core features will be disabled])]) +AM_CONDITIONAL([HAVE_LIBVIRT],[test "x$LIBVIRT_LIBS" != "x"]) dnl libxml2 (highly recommended) PKG_CHECK_MODULES([LIBXML2], [libxml-2.0], -- 1.7.3.2
Apparently Analagous Threads
- [PATCH 0/8 v2 DISCUSSION ONLY] Connecting to live virtual machines
- [PATCH 0/8 v2] Complete fix for CVE-2010-3851.
- NOTE: running ./fish/guestfish etc from build dir without installing
- [PATCH] Enable coredumps to be captured from the appliance (RHBZ#619334).
- trouble compiling libguestfs 1.17.21