2 * Copyright (C) 2011 Red Hat Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
32 #include "guestfs-internal.h"
33 #include "guestfs-internal-actions.h"
34 #include "guestfs_protocol.h"
36 static int read_whole_file (guestfs_h *g, const char *filename, char **data_r, size_t *size_r);
38 /* All these icon_*() functions return the same way. One of:
41 * An error occurred. Error has been set in the handle. The caller
42 * should return NULL immediately.
45 * Not an error, but no icon was found. 'ret' is just a dummy value
46 * which should be ignored (do not free it!)
48 * ret == ordinary pointer:
49 * An icon was found. 'ret' points to the icon buffer, and *size_r
52 static char *icon_favicon (guestfs_h *g, struct inspect_fs *fs, size_t *size_r);
53 static char *icon_fedora (guestfs_h *g, struct inspect_fs *fs, size_t *size_r);
54 static char *icon_rhel (guestfs_h *g, struct inspect_fs *fs, size_t *size_r);
55 static char *icon_debian (guestfs_h *g, struct inspect_fs *fs, size_t *size_r);
56 static char *icon_ubuntu (guestfs_h *g, struct inspect_fs *fs, size_t *size_r);
57 static char *icon_mageia (guestfs_h *g, struct inspect_fs *fs, size_t *size_r);
58 static char *icon_opensuse (guestfs_h *g, struct inspect_fs *fs, size_t *size_r);
59 static char *icon_windows (guestfs_h *g, struct inspect_fs *fs, size_t *size_r);
61 /* Dummy static object. */
62 static char *NOT_FOUND = (char *) "not_found";
64 /* For the unexpected legal consequences of this function, see:
65 * http://lists.fedoraproject.org/pipermail/legal/2011-April/001615.html
67 * Returns an RBufferOut, so the length of the returned buffer is
68 * returned in *size_r.
70 * Check optargs for the optional argument.
73 guestfs__inspect_get_icon (guestfs_h *g, const char *root, size_t *size_r,
74 const struct guestfs_inspect_get_icon_argv *optargs)
76 struct inspect_fs *fs;
78 int favicon, highquality;
81 fs = guestfs___search_for_root (g, root);
85 /* Get optargs, or defaults. */
87 optargs->bitmask & GUESTFS_INSPECT_GET_ICON_FAVICON_BITMASK ?
91 optargs->bitmask & GUESTFS_INSPECT_GET_ICON_HIGHQUALITY_BITMASK ?
92 optargs->highquality : 0;
94 /* Favicons are never high quality, so ... */
98 /* Try looking for a favicon first. */
100 r = icon_favicon (g, fs, &size);
104 if (r != NOT_FOUND) {
105 /* try_favicon succeeded in finding a favicon. */
111 /* Favicon failed, so let's try a method based on the detected operating
117 switch (fs->distro) {
118 case OS_DISTRO_FEDORA:
119 r = icon_fedora (g, fs, &size);
123 case OS_DISTRO_REDHAT_BASED:
124 case OS_DISTRO_CENTOS:
125 case OS_DISTRO_SCIENTIFIC_LINUX:
126 r = icon_rhel (g, fs, &size);
129 case OS_DISTRO_DEBIAN:
130 r = icon_debian (g, fs, &size);
133 case OS_DISTRO_UBUNTU:
134 r = icon_ubuntu (g, fs, &size);
137 case OS_DISTRO_MAGEIA:
138 r = icon_mageia (g, fs, &size);
141 case OS_DISTRO_OPENSUSE:
142 r = icon_opensuse(g, fs, &size);
145 /* These are just to keep gcc warnings happy. */
146 case OS_DISTRO_ARCHLINUX:
147 case OS_DISTRO_GENTOO:
148 case OS_DISTRO_LINUX_MINT:
149 case OS_DISTRO_MANDRIVA:
150 case OS_DISTRO_MEEGO:
151 case OS_DISTRO_PARDUS:
152 case OS_DISTRO_SLACKWARE:
153 case OS_DISTRO_TTYLINUX:
154 case OS_DISTRO_WINDOWS:
155 case OS_DISTRO_UNKNOWN:
160 case OS_TYPE_WINDOWS:
161 /* We don't know how to get high quality icons from a Windows guest,
162 * so disable this if high quality was specified.
165 r = icon_windows (g, fs, &size);
168 case OS_TYPE_FREEBSD:
170 case OS_TYPE_UNKNOWN:
174 if (r == NOT_FOUND) {
175 /* Not found, but not an error. So return the special zero-length
176 * buffer. Use malloc(1) here to ensure that malloc won't return
179 r = safe_malloc (g, 1);
187 /* Check that the named file 'filename' is a PNG file and is reasonable.
188 * If it is, download and return it.
191 get_png (guestfs_h *g, struct inspect_fs *fs, const char *filename,
192 size_t *size_r, size_t max_size)
194 char *ret = NOT_FOUND;
199 r = guestfs_exists (g, filename);
201 ret = NULL; /* a real error */
204 if (r == 0) goto out;
206 /* Check the file type and geometry. */
207 type = guestfs_file (g, filename);
210 if (!STRPREFIX (type, "PNG image data, ")) goto out;
211 if (sscanf (&type[16], "%d x %d", &w, &h) != 2) goto out;
212 if (w < 16 || h < 16 || w > 1024 || h > 1024) goto out;
214 /* Define a maximum reasonable size based on the geometry. This
215 * also limits the maximum we allocate below to around 4 MB.
218 max_size = 4 * w * h;
220 local = guestfs___download_to_tmp (g, fs, filename, "icon", max_size);
221 if (!local) goto out;
223 /* Successfully passed checks and downloaded. Read it into memory. */
224 if (read_whole_file (g, local, &ret, size_r) == -1) {
236 /* Return /etc/favicon.png (or \etc\favicon.png) if it exists and if
237 * it has a reasonable size and format.
240 icon_favicon (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
243 char *filename = safe_strdup (g, "/etc/favicon.png");
245 if (fs->type == OS_TYPE_WINDOWS) {
246 char *f = guestfs___case_sensitive_path_silently (g, filename);
253 ret = get_png (g, fs, filename, size_r, 0);
258 /* Return FEDORA_ICON. I checked that this exists on at least Fedora 6
261 #define FEDORA_ICON "/usr/share/icons/hicolor/96x96/apps/fedora-logo-icon.png"
264 icon_fedora (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
266 return get_png (g, fs, FEDORA_ICON, size_r, 0);
270 * /usr/share/pixmaps/redhat/shadowman-transparent.png is a 517x515
271 * PNG with alpha channel, around 64K in size.
274 * As above, but the file has been optimized to about 16K.
276 * Conveniently the RHEL clones also have the same file with the
277 * same name, but containing their own logos. Sense prevails!
279 #define SHADOWMAN_ICON "/usr/share/pixmaps/redhat/shadowman-transparent.png"
282 icon_rhel (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
286 if (fs->distro == OS_DISTRO_RHEL) {
287 if (fs->major_version <= 4)
293 return get_png (g, fs, SHADOWMAN_ICON, size_r, max_size);
296 #define DEBIAN_ICON "/usr/share/pixmaps/debian-logo.png"
299 icon_debian (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
301 return get_png (g, fs, DEBIAN_ICON, size_r, 2048);
304 #define UBUNTU_ICON "/usr/share/icons/gnome/24x24/places/ubuntu-logo.png"
307 icon_ubuntu (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
309 return get_png (g, fs, UBUNTU_ICON, size_r, 2048);
312 #define MAGEIA_ICON "/usr/share/icons/mageia.png"
315 icon_mageia (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
317 return get_png (g, fs, MAGEIA_ICON, size_r, 2048);
320 #define OPENSUSE_ICON "/usr/share/icons/hicolor/24x24/apps/distributor.png"
323 icon_opensuse (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
325 return get_png (g, fs, OPENSUSE_ICON, size_r, 2048);
328 /* Windows, as usual, has to be much more complicated and stupid than
331 * We have to download %systemroot%\explorer.exe and use a special
332 * program called 'wrestool' to extract the icons from this file. For
333 * each version of Windows, the icon we want is in a different place.
334 * The icon is in a stupid format (BMP), and in some cases multiple
335 * icons are in a single BMP file so we have to do some manipulation
338 * XXX I've only bothered with this nonsense for a few versions of
339 * Windows that I have handy. Please send patches to support other
344 icon_windows_xp (guestfs_h *g, struct inspect_fs *fs, const char *explorer,
352 pngfile = safe_asprintf (g, "%s/windows-xp-icon.png", g->tmpdir);
354 cmd = safe_asprintf (g,
355 "wrestool -x --type=2 --name=143 %s | "
356 "bmptopnm | pnmtopng > %s",
359 if (r == -1 || WEXITSTATUS (r) != 0) {
360 debug (g, "external command failed: %s", cmd);
368 if (read_whole_file (g, pngfile, &ret, size_r) == -1) {
379 icon_windows_7 (guestfs_h *g, struct inspect_fs *fs, const char *explorer,
387 pngfile = safe_asprintf (g, "%s/windows-7-icon.png", g->tmpdir);
389 cmd = safe_asprintf (g,
390 "wrestool -x --type=2 --name=6801 %s | "
391 "bmptopnm | pamcut -bottom 54 | pnmtopng > %s",
394 if (r == -1 || WEXITSTATUS (r) != 0) {
395 debug (g, "external command failed: %s", cmd);
403 if (read_whole_file (g, pngfile, &ret, size_r) == -1) {
414 icon_windows (guestfs_h *g, struct inspect_fs *fs, size_t *size_r)
416 char *(*fn) (guestfs_h *g, struct inspect_fs *fs, const char *explorer,
418 char *filename1, *filename2, *filename3;
422 if (fs->major_version == 5 && fs->minor_version == 1)
423 fn = icon_windows_xp;
426 else if (fs->major_version == 6 && fs->minor_version == 1)
429 /* Not (yet) a supported version of Windows. */
430 else return NOT_FOUND;
432 if (fs->windows_systemroot == NULL)
435 /* Download %systemroot%\explorer.exe */
436 filename1 = safe_asprintf (g, "%s/explorer.exe", fs->windows_systemroot);
437 filename2 = guestfs___case_sensitive_path_silently (g, filename1);
439 if (filename2 == NULL)
442 filename3 = guestfs___download_to_tmp (g, fs, filename2, "explorer",
443 MAX_WINDOWS_EXPLORER_SIZE);
445 if (filename3 == NULL)
448 ret = fn (g, fs, filename3, size_r);
453 /* Read the whole file into a memory buffer and return it. The file
454 * should be a regular, local, trusted file.
457 read_whole_file (guestfs_h *g, const char *filename,
458 char **data_r, size_t *size_r)
467 fd = open (filename, O_RDONLY);
469 perrorf (g, "open: %s", filename);
473 if (fstat (fd, &statbuf) == -1) {
474 perrorf (g, "stat: %s", filename);
479 size = statbuf.st_size;
480 data = safe_malloc (g, size);
484 r = read (fd, &data[n], size - n);
486 perrorf (g, "read: %s", filename);
492 error (g, _("read: %s: unexpected end of file"), filename);
500 if (close (fd) == -1) {
501 perrorf (g, "close: %s", filename);