summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard W.M. Jones <rjones@redhat.com>2011-06-27 15:27:46 +0100
committerRichard W.M. Jones <rjones@redhat.com>2011-06-28 18:41:54 +0100
commit7f16c346bbeba2f2fe3c31ccb85158178a284d84 (patch)
tree6d69f6b20598b05e8aa6eabeba96c40ade38f9f7
parent5f26270c343bf543a7bf20cf3e6f182f6282f8ea (diff)
downloadlibguestfs-7f16c346bbeba2f2fe3c31ccb85158178a284d84.tar.gz
libguestfs-7f16c346bbeba2f2fe3c31ccb85158178a284d84.tar.xz
libguestfs-7f16c346bbeba2f2fe3c31ccb85158178a284d84.zip
New API: inspect-get-icon returns the guest icon.
This API returns the guest's favicon if found, else an icon representing the guest operating system. Currently supported by this patch: Fedora, RHEL and derivatives, Debian (but not Ubuntu), Windows XP, Windows 7. This also updates virt-inspector to include an <icon> element containing the icon in base64 encoding.
-rw-r--r--TODO7
-rw-r--r--generator/generator_actions.ml64
-rw-r--r--inspector/virt-inspector.c32
-rw-r--r--inspector/virt-inspector.rng6
-rw-r--r--po/POTFILES.in1
-rw-r--r--src/Makefile.am1
-rw-r--r--src/guestfs-internal.h3
-rw-r--r--src/inspect_icon.c470
8 files changed, 569 insertions, 15 deletions
diff --git a/TODO b/TODO
index c5eab929..3a0a6b05 100644
--- a/TODO
+++ b/TODO
@@ -441,13 +441,6 @@ More inspection features
- last user who logged in
- lastlog, last, who
-Get the guest icon
-------------------
-
-- For Linux guests, use /etc/favicon.png if available, else get it in
- a distro-specific manner.
-- For Windows guests, parse it out of c:\windows\explorer.exe
-
Integrate virt-inspector with CMDBs
-----------------------------------
diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml
index fa2ff08b..d741dfb5 100644
--- a/generator/generator_actions.ml
+++ b/generator/generator_actions.ml
@@ -1462,6 +1462,70 @@ Please read L<guestfs(3)/INSPECTION> for more details.
See also C<guestfs_inspect_get_mountpoints>,
C<guestfs_inspect_get_filesystems>.");
+ ("inspect_get_icon", (RBufferOut "icon", [Device "root"], [Bool "favicon"; Bool "highquality"]), -1, [],
+ [],
+ "get the icon corresponding to this operating system",
+ "\
+This function returns an icon corresponding to the inspected
+operating system. The icon is returned as a buffer containing a
+PNG image (re-encoded to PNG if necessary).
+
+If it was not possible to get an icon this function returns a
+zero-length (non-NULL) buffer. I<Callers must check for this case>.
+
+Libguestfs will start by looking for a file called
+C</etc/favicon.png> or C<C:\\etc\\favicon.png>
+and if it has the correct format, the contents of this file will
+be returned. You can disable favicons by passing the
+optional C<favicon> boolean as false (default is true).
+
+If finding the favicon fails, then we look in other places in the
+guest for a suitable icon.
+
+If the optional C<highquality> boolean is true then
+only high quality icons are returned, which means only icons of
+high resolution with an alpha channel. The default (false) is
+to return any icon we can, even if it is of substandard quality.
+
+Notes:
+
+=over 4
+
+=item *
+
+Unlike most other inspection API calls, the guest's disks must be
+mounted up before you call this, since it needs to read information
+from the guest filesystem during the call.
+
+=item *
+
+B<Security:> The icon data comes from the untrusted guest,
+and should be treated with caution. PNG files have been
+known to contain exploits. Ensure that libpng (or other relevant
+libraries) are fully up to date before trying to process or
+display the icon.
+
+=item *
+
+The PNG image returned can be any size. It might not be square.
+Libguestfs tries to return the largest, highest quality
+icon available. The application must scale the icon to the
+required size.
+
+=item *
+
+Extracting icons from Windows guests requires the external
+C<wrestool> program from the C<icoutils> package, and
+several programs (C<bmptopnm>, C<pnmtopng>, C<pamcut>)
+from the C<netpbm> package. These must be installed separately.
+
+=item *
+
+Operating system icons are usually trademarks. Seek legal
+advice before using trademarks in applications.
+
+=back");
+
]
(* daemon_functions are any functions which cause some action
diff --git a/inspector/virt-inspector.c b/inspector/virt-inspector.c
index 607e5ae0..4afce0e2 100644
--- a/inspector/virt-inspector.c
+++ b/inspector/virt-inspector.c
@@ -340,6 +340,7 @@ output_root (xmlTextWriterPtr xo, char *root)
int i, r;
char buf[32];
char canonical_root[strlen (root) + 1];
+ size_t size;
XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "operatingsystem"));
@@ -471,8 +472,32 @@ output_root (xmlTextWriterPtr xo, char *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));
}
@@ -652,19 +677,12 @@ 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"));
diff --git a/inspector/virt-inspector.rng b/inspector/virt-inspector.rng
index 480467ed..4fd208a3 100644
--- a/inspector/virt-inspector.rng
+++ b/inspector/virt-inspector.rng
@@ -1,4 +1,6 @@
-<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+<grammar
+ xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<!-- -*- xml -*-
This is a RELAX NG schema for the output of 'virt-inspector'.
@@ -51,6 +53,8 @@
<optional><ref name="drive_mappings"/></optional>
<optional><ref name="applications"/></optional>
+ <optional><element name="icon"><data type="base64Binary"/></element></optional>
+
</interleave>
</element>
</oneOrMore>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index da47c913..c3f671a9 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -147,6 +147,7 @@ src/inspect_fs.c
src/inspect_fs_cd.c
src/inspect_fs_unix.c
src/inspect_fs_windows.c
+src/inspect_icon.c
src/launch.c
src/listfs.c
src/match.c
diff --git a/src/Makefile.am b/src/Makefile.am
index c2e0c6e5..6c0cc33d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -133,6 +133,7 @@ libguestfs_la_SOURCES = \
inspect_fs_cd.c \
inspect_fs_unix.c \
inspect_fs_windows.c \
+ inspect_icon.c \
launch.c \
listfs.c \
match.c \
diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h
index 0650eb29..96f81525 100644
--- a/src/guestfs-internal.h
+++ b/src/guestfs-internal.h
@@ -89,6 +89,9 @@
*/
#define MAX_PKG_DB_SIZE (300 * 1000 * 1000)
+/* Maximum size of Windows explorer.exe. 2.6MB on Windows 7. */
+#define MAX_WINDOWS_EXPLORER_SIZE (4 * 1000 * 1000)
+
/* Network configuration of the appliance. Note these addresses are
* only meaningful within the context of the running appliance. QEMU
* translates network connections to these magic addresses into
diff --git a/src/inspect_icon.c b/src/inspect_icon.c
new file mode 100644
index 00000000..ad0f62ce
--- /dev/null
+++ b/src/inspect_icon.c
@@ -0,0 +1,470 @@
+/* libguestfs
+ * Copyright (C) 2011 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 <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include "guestfs.h"
+#include "guestfs-internal.h"
+#include "guestfs-internal-actions.h"
+#include "guestfs_protocol.h"
+
+static int read_whole_file (guestfs_h *g, const char *filename, char **data_r, size_t *size_r);
+
+/* All these icon_*() functions return the same way. One of:
+ *
+ * ret == NULL:
+ * An error occurred. Error has been set in the handle. The caller
+ * should return NULL immediately.
+ *
+ * ret == NOT_FOUND:
+ * Not an error, but no icon was found. 'ret' is just a dummy value
+ * which should be ignored (do not free it!)
+ *
+ * ret == ordinary pointer:
+ * An icon was found. 'ret' points to the icon buffer, and *size_r
+ * is the size.
+ */
+static char *icon_favicon (guestfs_h *g, struct inspect_fs *fs, size_t *size_r);
+static char *icon_fedora (guestfs_h *g, struct inspect_fs *fs, size_t *size_r);
+static char *icon_rhel (guestfs_h *g, struct inspect_fs *fs, size_t *size_r);
+static char *icon_debian (guestfs_h *g, struct inspect_fs *fs, size_t *size_r);
+static char *icon_windows (guestfs_h *g, struct inspect_fs *fs, size_t *size_r);
+
+/* Dummy static object. */
+static char *NOT_FOUND = (char *) "not_found";
+
+/* For the unexpected legal consequences of this function, see:
+ * http://lists.fedoraproject.org/pipermail/legal/2011-April/001615.html
+ *
+ * Returns an RBufferOut, so the length of the returned buffer is
+ * returned in *size_r.
+ *
+ * Check optargs for the optional argument.
+ */
+char *
+guestfs__inspect_get_icon (guestfs_h *g, const char *root, size_t *size_r,
+ const struct guestfs_inspect_get_icon_argv *optargs)
+{
+ struct inspect_fs *fs;
+ char *r = NOT_FOUND;
+ int favicon, highquality;
+ size_t size;
+
+ fs = guestfs___search_for_root (g, root);
+ if (!fs)
+ return NULL;
+
+ /* Get optargs, or defaults. */
+ favicon =
+ optargs->bitmask & GUESTFS_INSPECT_GET_ICON_FAVICON_BITMASK ?
+ optargs->favicon : 1;
+
+ highquality =
+ optargs->bitmask & GUESTFS_INSPECT_GET_ICON_HIGHQUALITY_BITMASK ?
+ optargs->highquality : 0;
+
+ /* Favicons are never high quality, so ... */
+ if (highquality)
+ favicon = 0;
+
+ /* Try looking for a favicon first. */
+ if (favicon) {
+ r = icon_favicon (g, fs, &size);
+ if (!r)
+ return NULL;
+
+ if (r != NOT_FOUND) {
+ /* try_favicon succeeded in finding a favicon. */
+ *size_r = size;
+ return r;
+ }
+ }
+
+ /* Favicon failed, so let's try a method based on the detected operating
+ * system.
+ */
+ switch (fs->type) {
+ case OS_TYPE_LINUX:
+ switch (fs->distro) {
+ case OS_DISTRO_FEDORA:
+ r = icon_fedora (g, fs, &size);
+ break;
+
+ case OS_DISTRO_RHEL:
+ case OS_DISTRO_REDHAT_BASED:
+ case OS_DISTRO_CENTOS:
+ case OS_DISTRO_SCIENTIFIC_LINUX:
+ r = icon_rhel (g, fs, &size);
+ break;
+
+ case OS_DISTRO_DEBIAN:
+ r = icon_debian (g, fs, &size);
+ break;
+
+ /* These are just to keep gcc warnings happy. */
+ case OS_DISTRO_ARCHLINUX:
+ case OS_DISTRO_GENTOO:
+ case OS_DISTRO_LINUX_MINT:
+ case OS_DISTRO_MANDRIVA:
+ case OS_DISTRO_MEEGO:
+ case OS_DISTRO_PARDUS:
+ case OS_DISTRO_SLACKWARE:
+ case OS_DISTRO_UBUNTU:
+ case OS_DISTRO_WINDOWS:
+ case OS_DISTRO_UNKNOWN:
+ default: ;
+ }
+ break;
+
+ case OS_TYPE_WINDOWS:
+ /* We don't know how to get high quality icons from a Windows guest,
+ * so disable this if high quality was specified.
+ */
+ if (!highquality)
+ r = icon_windows (g, fs, &size);
+ break;
+
+ case OS_TYPE_FREEBSD:
+ case OS_TYPE_UNKNOWN:
+ default: ;
+ }
+
+ if (r == NOT_FOUND) {
+ /* Not found, but not an error. So return the special zero-length
+ * buffer. Use malloc(1) here to ensure that malloc won't return
+ * NULL.
+ */
+ r = safe_malloc (g, 1);
+ size = 0;
+ }
+
+ *size_r = size;
+ return r;
+}
+
+/* Check that the named file 'filename' is a PNG file and is reasonable.
+ * If it is, download and return it.
+ */
+static char *
+get_png (guestfs_h *g, struct inspect_fs *fs, const char *filename,
+ size_t *size_r, size_t max_size)
+{
+ char *ret = NOT_FOUND;
+ char *type = NULL;
+ char *local = NULL;
+ int r, w, h;
+
+ r = guestfs_exists (g, filename);
+ if (r == -1) {
+ ret = NULL; /* a real error */
+ goto out;
+ }
+ if (r == 0) goto out;
+
+ /* Check the file type and geometry. */
+ type = guestfs_file (g, filename);
+ if (!type) goto out;
+
+ if (!STRPREFIX (type, "PNG image data, ")) goto out;
+ if (sscanf (&type[16], "%d x %d", &w, &h) != 2) goto out;
+ if (w < 16 || h < 16 || w > 1024 || h > 1024) goto out;
+
+ /* Define a maximum reasonable size based on the geometry. This
+ * also limits the maximum we allocate below to around 4 MB.
+ */
+ if (max_size == 0)
+ max_size = 4 * w * h;
+
+ local = guestfs___download_to_tmp (g, fs, filename, "icon", max_size);
+ if (!local) goto out;
+
+ /* Successfully passed checks and downloaded. Read it into memory. */
+ if (read_whole_file (g, local, &ret, size_r) == -1) {
+ ret = NULL;
+ goto out;
+ }
+
+ out:
+ free (local);
+ free (type);
+
+ return ret;
+}
+
+/* Return /etc/favicon.png (or \etc\favicon.png) if it exists and if
+ * it has a reasonable size and format.
+ */
+static char *
+icon_favicon (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
+{
+ char *ret;
+ char *filename = safe_strdup (g, "/etc/favicon.png");
+
+ if (fs->type == OS_TYPE_WINDOWS) {
+ char *f = guestfs___case_sensitive_path_silently (g, filename);
+ if (f) {
+ free (filename);
+ filename = f;
+ }
+ }
+
+ ret = get_png (g, fs, filename, size_r, 0);
+ free (filename);
+ return ret;
+}
+
+/* Return FEDORA_ICON. I checked that this exists on at least Fedora 6
+ * through 16.
+ */
+#define FEDORA_ICON "/usr/share/icons/hicolor/96x96/apps/fedora-logo-icon.png"
+
+static char *
+icon_fedora (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
+{
+ return get_png (g, fs, FEDORA_ICON, size_r, 0);
+}
+
+/* RHEL 3, 4:
+ * /usr/share/pixmaps/redhat/shadowman-transparent.png is a 517x515
+ * PNG with alpha channel, around 64K in size.
+ *
+ * RHEL 5, 6:
+ * As above, but the file has been optimized to about 16K.
+ *
+ * Conveniently the RHEL clones also have the same file with the
+ * same name, but containing their own logos. Sense prevails!
+ */
+#define SHADOWMAN_ICON "/usr/share/pixmaps/redhat/shadowman-transparent.png"
+
+static char *
+icon_rhel (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
+{
+ size_t max_size = 0;
+
+ if (fs->distro == OS_DISTRO_RHEL) {
+ if (fs->major_version <= 4)
+ max_size = 66000;
+ else
+ max_size = 17000;
+ }
+
+ return get_png (g, fs, SHADOWMAN_ICON, size_r, max_size);
+}
+
+/* NB: I've not located an Ubuntu logo yet. */
+#define DEBIAN_ICON "/usr/share/pixmaps/debian-logo.png"
+
+static char *
+icon_debian (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
+{
+ return get_png (g, fs, DEBIAN_ICON, size_r, 2048);
+}
+
+/* Windows, as usual, has to be much more complicated and stupid than
+ * anything else.
+ *
+ * We have to download %systemroot%\explorer.exe and use a special
+ * program called 'wrestool' to extract the icons from this file. For
+ * each version of Windows, the icon we want is in a different place.
+ * The icon is in a stupid format (BMP), and in some cases multiple
+ * icons are in a single BMP file so we have to do some manipulation
+ * on the file.
+ *
+ * XXX I've only bothered with this nonsense for a few versions of
+ * Windows that I have handy. Please send patches to support other
+ * versions.
+ */
+
+static char *
+icon_windows_xp (guestfs_h *g, struct inspect_fs *fs, const char *explorer,
+ size_t *size_r)
+{
+ char *ret;
+ char *pngfile;
+ char *cmd;
+ int r;
+
+ pngfile = safe_asprintf (g, "%s/windows-xp-icon.png", g->tmpdir);
+
+ cmd = safe_asprintf (g,
+ "wrestool -x --type=2 --name=143 %s | "
+ "bmptopnm | pnmtopng > %s",
+ explorer, pngfile);
+ r = system (cmd);
+ if (r == -1 || WEXITSTATUS (r) != 0) {
+ debug (g, "external command failed: %s", cmd);
+ free (cmd);
+ free (pngfile);
+ return NOT_FOUND;
+ }
+
+ free (cmd);
+
+ if (read_whole_file (g, pngfile, &ret, size_r) == -1) {
+ free (pngfile);
+ return NULL;
+ }
+
+ free (pngfile);
+
+ return ret;
+}
+
+static char *
+icon_windows_7 (guestfs_h *g, struct inspect_fs *fs, const char *explorer,
+ size_t *size_r)
+{
+ char *ret;
+ char *pngfile;
+ char *cmd;
+ int r;
+
+ pngfile = safe_asprintf (g, "%s/windows-7-icon.png", g->tmpdir);
+
+ cmd = safe_asprintf (g,
+ "wrestool -x --type=2 --name=6801 %s | "
+ "bmptopnm | pamcut -bottom 54 | pnmtopng > %s",
+ explorer, pngfile);
+ r = system (cmd);
+ if (r == -1 || WEXITSTATUS (r) != 0) {
+ debug (g, "external command failed: %s", cmd);
+ free (cmd);
+ free (pngfile);
+ return NOT_FOUND;
+ }
+
+ free (cmd);
+
+ if (read_whole_file (g, pngfile, &ret, size_r) == -1) {
+ free (pngfile);
+ return NULL;
+ }
+
+ free (pngfile);
+
+ return ret;
+}
+
+static char *
+icon_windows (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
+{
+ char *(*fn) (guestfs_h *g, struct inspect_fs *fs, const char *explorer,
+ size_t *size_r);
+ char *filename1, *filename2, *filename3;
+ char *ret;
+
+ /* Windows XP. */
+ if (fs->major_version == 5 && fs->minor_version == 1)
+ fn = icon_windows_xp;
+
+ /* Windows 7 */
+ else if (fs->major_version == 6 && fs->minor_version == 1)
+ fn = icon_windows_7;
+
+ /* Not (yet) a supported version of Windows. */
+ else return NOT_FOUND;
+
+ if (fs->windows_systemroot == NULL)
+ return NOT_FOUND;
+
+ /* Download %systemroot%\explorer.exe */
+ filename1 = safe_asprintf (g, "%s/explorer.exe", fs->windows_systemroot);
+ filename2 = guestfs___case_sensitive_path_silently (g, filename1);
+ free (filename1);
+ if (filename2 == NULL)
+ return NOT_FOUND;
+
+ filename3 = guestfs___download_to_tmp (g, fs, filename2, "explorer",
+ MAX_WINDOWS_EXPLORER_SIZE);
+ free (filename2);
+ if (filename3 == NULL)
+ return NOT_FOUND;
+
+ ret = fn (g, fs, filename3, size_r);
+ free (filename3);
+ return ret;
+}
+
+/* Read the whole file into a memory buffer and return it. The file
+ * should be a regular, local, trusted file.
+ */
+static int
+read_whole_file (guestfs_h *g, const char *filename,
+ char **data_r, size_t *size_r)
+{
+ int fd;
+ char *data;
+ off_t size;
+ off_t n;
+ ssize_t r;
+ struct stat statbuf;
+
+ fd = open (filename, O_RDONLY);
+ if (fd == -1) {
+ perrorf (g, "open: %s", filename);
+ return -1;
+ }
+
+ if (fstat (fd, &statbuf) == -1) {
+ perrorf (g, "stat: %s", filename);
+ close (fd);
+ return -1;
+ }
+
+ size = statbuf.st_size;
+ data = safe_malloc (g, size);
+
+ n = 0;
+ while (n < size) {
+ r = read (fd, &data[n], size - n);
+ if (r == -1) {
+ perrorf (g, "read: %s", filename);
+ free (data);
+ close (fd);
+ return -1;
+ }
+ if (r == 0) {
+ error (g, _("read: %s: unexpected end of file"), filename);
+ free (data);
+ close (fd);
+ return -1;
+ }
+ n += r;
+ }
+
+ if (close (fd) == -1) {
+ perrorf (g, "close: %s", filename);
+ free (data);
+ return -1;
+ }
+
+ *data_r = data;
+ *size_r = size;
+
+ return 0;
+}