X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=inspector%2Fvirt-inspector.c;h=09621abb54002a8dc22fb9ffa7dea0992460c2ae;hp=a3c0a04a304a24355f4e714b671eaa817910eba2;hb=HEAD;hpb=c5cb65f0aac3298e634b183f73fda6644a158018 diff --git a/inspector/virt-inspector.c b/inspector/virt-inspector.c index a3c0a04..09621ab 100644 --- a/inspector/virt-inspector.c +++ b/inspector/virt-inspector.c @@ -1,5 +1,5 @@ /* virt-inspector - * Copyright (C) 2010 Red Hat Inc. + * Copyright (C) 2010-2011 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 @@ -13,20 +13,27 @@ * * 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. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include +#include #include #include #include +#include #include +#include #include #include +#include +#include +#include +#include #include "progname.h" #include "c-ctype.h" @@ -38,21 +45,25 @@ guestfs_h *g; int read_only = 1; +int live = 0; int verbose = 0; int keys_from_stdin = 0; int echo_keys = 0; const char *libvirt_uri = NULL; int inspector = 1; +static const char *xpath = NULL; static void output (char **roots); static void output_roots (xmlTextWriterPtr xo, char **roots); static void output_root (xmlTextWriterPtr xo, char *root); static void output_mountpoints (xmlTextWriterPtr xo, char *root); static void output_filesystems (xmlTextWriterPtr xo, char *root); +static void output_drive_mappings (xmlTextWriterPtr xo, char *root); static void output_applications (xmlTextWriterPtr xo, char *root); static void canonicalize (char *dev); static void free_strings (char **argv); static int count_strings (char *const*argv); +static void do_xpath (const char *query); static inline char * bad_cast (char const *s) @@ -83,7 +94,8 @@ usage (int status) " --keys-from-stdin Read passphrases from stdin\n" " -v|--verbose Verbose messages\n" " -V|--version Display version and exit\n" - " -x Echo each command before executing it\n" + " -x Trace libguestfs API calls\n" + " --xpath query Perform an XPath query\n" "For more information, see the manpage %s(1).\n"), program_name, program_name, program_name, program_name); @@ -114,15 +126,14 @@ main (int argc, char *argv[]) { "keys-from-stdin", 0, 0, 0 }, { "verbose", 0, 0, 'v' }, { "version", 0, 0, 'V' }, + { "xpath", 1, 0, 0 }, { 0, 0, 0, 0 } }; struct drv *drvs = NULL; struct drv *drv; - char *p, *file = NULL; const char *format = NULL; int c; int option_index; - int next_prepared_drive = 1; g = guestfs_create (); if (g == NULL) { @@ -147,6 +158,8 @@ main (int argc, char *argv[]) format = NULL; else format = optarg; + } else if (STREQ (long_options[option_index].name, "xpath")) { + xpath = optarg; } else { fprintf (stderr, _("%s: unknown long option: %s (%d)\n"), program_name, long_options[option_index].name, option_index); @@ -228,11 +241,27 @@ main (int argc, char *argv[]) */ assert (read_only == 1); assert (inspector == 1); + assert (live == 0); /* Must be no extra arguments on the command line. */ if (optind != argc) usage (EXIT_FAILURE); + /* XPath is modal: no drives should be specified. There must be + * one extra parameter on the command line. + */ + if (xpath) { + if (drvs != NULL) { + fprintf (stderr, _("%s: cannot use --xpath together with other options.\n"), + program_name); + exit (EXIT_FAILURE); + } + + do_xpath (xpath); + + exit (EXIT_SUCCESS); + } + /* User must have specified some drives. */ if (drvs == NULL) usage (EXIT_FAILURE); @@ -333,9 +362,10 @@ static void output_root (xmlTextWriterPtr xo, char *root) { char *str; - int i; + int i, r; char buf[32]; char canonical_root[strlen (root) + 1]; + size_t size; XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "operatingsystem")); @@ -372,6 +402,13 @@ output_root (xmlTextWriterPtr xo, char *root) xmlTextWriterWriteElement (xo, BAD_CAST "product_name", BAD_CAST str)); free (str); + str = guestfs_inspect_get_product_variant (g, root); + if (!str) exit (EXIT_FAILURE); + if (STRNEQ (str, "unknown")) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "product_variant", BAD_CAST str)); + free (str); + i = guestfs_inspect_get_major_version (g, root); snprintf (buf, sizeof buf, "%d", i); XMLERROR (-1, @@ -408,13 +445,84 @@ output_root (xmlTextWriterPtr xo, char *root) BAD_CAST str)); free (str); ); + DISABLE_GUESTFS_ERRORS_FOR ( + str = guestfs_inspect_get_windows_current_control_set (g, root); + if (str) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "windows_current_control_set", + BAD_CAST str)); + free (str); + ); + + str = guestfs_inspect_get_hostname (g, root); + if (!str) exit (EXIT_FAILURE); + if (STRNEQ (str, "unknown")) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "hostname", + BAD_CAST str)); + free (str); + + str = guestfs_inspect_get_format (g, root); + if (!str) exit (EXIT_FAILURE); + if (STRNEQ (str, "unknown")) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "format", + BAD_CAST str)); + free (str); + + r = guestfs_inspect_is_live (g, root); + if (r > 0) { + XMLERROR (-1, + xmlTextWriterStartElement (xo, BAD_CAST "live")); + XMLERROR (-1, xmlTextWriterEndElement (xo)); + } + + r = guestfs_inspect_is_netinst (g, root); + if (r > 0) { + XMLERROR (-1, + xmlTextWriterStartElement (xo, BAD_CAST "netinst")); + XMLERROR (-1, xmlTextWriterEndElement (xo)); + } + + r = guestfs_inspect_is_multipart (g, root); + if (r > 0) { + XMLERROR (-1, + xmlTextWriterStartElement (xo, BAD_CAST "multipart")); + XMLERROR (-1, xmlTextWriterEndElement (xo)); + } output_mountpoints (xo, root); output_filesystems (xo, root); + output_drive_mappings (xo, root); + + /* We need to mount everything up in order to read out the list of + * applications and the icon, ie. everything below this point. + */ + inspect_mount_root (root); + output_applications (xo, root); + /* Don't return favicon. XXX Should we? */ + str = guestfs_inspect_get_icon (g, root, &size, + GUESTFS_INSPECT_GET_ICON_FAVICON, 0, + -1); + if (!str) exit (EXIT_FAILURE); + if (size > 0) { + XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "icon")); + XMLERROR (-1, xmlTextWriterWriteBase64 (xo, str, 0, size)); + XMLERROR (-1, xmlTextWriterEndElement (xo)); + } + /* Note we must free (str) even if size == 0, because that indicates + * there was no icon. + */ + free (str); + + /* Unmount (see inspect_mount_root above). */ + if (guestfs_umount_all (g) == -1) + exit (EXIT_FAILURE); + XMLERROR (-1, xmlTextWriterEndElement (xo)); } @@ -428,6 +536,15 @@ compare_keys (const void *p1, const void *p2) } static int +compare_keys_nocase (const void *p1, const void *p2) +{ + const char *key1 = * (char * const *) p1; + const char *key2 = * (char * const *) p2; + + return strcasecmp (key1, key2); +} + +static int compare_keys_len (const void *p1, const void *p2) { const char *key1 = * (char * const *) p1; @@ -538,24 +655,59 @@ output_filesystems (xmlTextWriterPtr xo, char *root) } static void +output_drive_mappings (xmlTextWriterPtr xo, char *root) +{ + char **drive_mappings = NULL; + size_t i; + + DISABLE_GUESTFS_ERRORS_FOR ( + drive_mappings = guestfs_inspect_get_drive_mappings (g, root); + ); + if (drive_mappings == NULL) + return; + + if (drive_mappings[0] == NULL) { + free_strings (drive_mappings); + return; + } + + /* Sort by key. */ + qsort (drive_mappings, + count_strings (drive_mappings) / 2, 2 * sizeof (char *), + compare_keys_nocase); + + XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "drive_mappings")); + + for (i = 0; drive_mappings[i] != NULL; i += 2) { + canonicalize (drive_mappings[i+1]); + + XMLERROR (-1, + xmlTextWriterStartElement (xo, BAD_CAST "drive_mapping")); + XMLERROR (-1, + xmlTextWriterWriteAttribute (xo, BAD_CAST "name", + BAD_CAST drive_mappings[i])); + XMLERROR (-1, + xmlTextWriterWriteString (xo, BAD_CAST drive_mappings[i+1])); + XMLERROR (-1, xmlTextWriterEndElement (xo)); + } + + XMLERROR (-1, xmlTextWriterEndElement (xo)); + + free_strings (drive_mappings); +} + +static void output_applications (xmlTextWriterPtr xo, char *root) { struct guestfs_application_list *apps; size_t i; - /* We need to mount everything up in order to read out the list of - * applications. - */ - inspect_mount_root (root); - /* This returns an empty list if we simply couldn't determine the * applications, so if it returns NULL then it's a real error. */ apps = guestfs_inspect_list_applications (g, root); if (apps == NULL) exit (EXIT_FAILURE); - if (guestfs_umount_all (g) == -1) - exit (EXIT_FAILURE); XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "applications")); @@ -632,7 +784,7 @@ canonicalize (char *dev) (dev[5] == 'h' || dev[5] == 'v') && dev[6] == 'd' && c_isalpha (dev[7]) && - c_isdigit (dev[8])) + (c_isdigit (dev[8]) || dev[8] == '\0')) dev[5] = 's'; } @@ -655,3 +807,102 @@ count_strings (char *const *argv) ; return c; } + +/* Run an XPath query on XML on stdin, print results to stdout. */ +static void +do_xpath (const char *query) +{ + xmlDocPtr doc; + xmlXPathContextPtr xpathCtx; + xmlXPathObjectPtr xpathObj; + xmlNodeSetPtr nodes; + char *r; + size_t i; + xmlSaveCtxtPtr saveCtx; + xmlDocPtr wrdoc; + xmlNodePtr wrnode; + + doc = xmlReadFd (STDIN_FILENO, NULL, "utf8", 0); + if (doc == NULL) { + fprintf (stderr, _("%s: unable to parse XML from stdin\n"), program_name); + exit (EXIT_FAILURE); + } + + xpathCtx = xmlXPathNewContext (doc); + if (xpathCtx == NULL) { + fprintf (stderr, _("%s: unable to create new XPath context\n"), + program_name); + exit (EXIT_FAILURE); + } + + xpathObj = xmlXPathEvalExpression (BAD_CAST query, xpathCtx); + if (xpathObj == NULL) { + fprintf (stderr, _("%s: unable to evaluate XPath expression\n"), + program_name); + exit (EXIT_FAILURE); + } + + switch (xpathObj->type) { + case XPATH_NODESET: + nodes = xpathObj->nodesetval; + + saveCtx = xmlSaveToFd (STDOUT_FILENO, NULL, XML_SAVE_NO_DECL); + if (saveCtx == NULL) { + fprintf (stderr, _("%s: xmlSaveToFd failed\n"), program_name); + exit (EXIT_FAILURE); + } + + for (i = 0; i < (size_t) nodes->nodeNr; ++i) { + wrdoc = xmlNewDoc (BAD_CAST "1.0"); + if (wrdoc == NULL) { + fprintf (stderr, _("%s: xmlNewDoc failed\n"), program_name); + exit (EXIT_FAILURE); + } + wrnode = xmlCopyNode (nodes->nodeTab[i], 1); + if (wrnode == NULL) { + fprintf (stderr, _("%s: xmlCopyNode failed\n"), program_name); + exit (EXIT_FAILURE); + } + + xmlDocSetRootElement (wrdoc, wrnode); + + if (xmlSaveDoc (saveCtx, wrdoc) == -1) { + fprintf (stderr, _("%s: xmlSaveDoc failed\n"), program_name); + exit (EXIT_FAILURE); + } + + xmlFreeDoc (wrdoc); + } + + xmlSaveClose (saveCtx); + + break; + + case XPATH_STRING: + r = (char *) xpathObj->stringval; + printf ("%s", r); + i = strlen (r); + if (i > 0 && r[i-1] != '\n') + printf ("\n"); + break; + + case XPATH_UNDEFINED: /* grrrrr ... switch-enum is a useless warning */ + case XPATH_BOOLEAN: + case XPATH_NUMBER: + case XPATH_POINT: + case XPATH_RANGE: + case XPATH_LOCATIONSET: + case XPATH_USERS: + case XPATH_XSLT_TREE: + default: + r = (char *) xmlXPathCastToString (xpathObj); + printf ("%s\n", r); + free (r); + } + + xmlXPathFreeObject (xpathObj); + xmlXPathFreeContext (xpathCtx); + xmlFreeDoc (doc); + + exit (EXIT_SUCCESS); +}