2 * Copyright (C) 2010-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
39 #include "ignore-value.h"
43 #include "guestfs-internal.h"
44 #include "guestfs-internal-actions.h"
45 #include "guestfs_protocol.h"
47 #if defined(HAVE_HIVEX)
50 static struct guestfs_application_list *list_applications_rpm (guestfs_h *g, struct inspect_fs *fs);
52 static struct guestfs_application_list *list_applications_deb (guestfs_h *g, struct inspect_fs *fs);
53 static struct guestfs_application_list *list_applications_windows (guestfs_h *g, struct inspect_fs *fs);
54 static void add_application (guestfs_h *g, struct guestfs_application_list *, const char *name, const char *display_name, int32_t epoch, const char *version, const char *release, const char *install_path, const char *publisher, const char *url, const char *description);
55 static void sort_applications (struct guestfs_application_list *);
57 /* Unlike the simple inspect-get-* calls, this one assumes that the
58 * disks are mounted up, and reads files from the mounted disks.
60 struct guestfs_application_list *
61 guestfs__inspect_list_applications (guestfs_h *g, const char *root)
63 struct inspect_fs *fs = guestfs___search_for_root (g, root);
67 struct guestfs_application_list *ret = NULL;
69 /* Presently we can only list applications for installed disks. It
70 * is possible in future to get lists of packages from installers.
72 if (fs->format == OS_FORMAT_INSTALLED) {
75 switch (fs->package_format) {
76 case OS_PACKAGE_FORMAT_RPM:
78 ret = list_applications_rpm (g, fs);
84 case OS_PACKAGE_FORMAT_DEB:
85 ret = list_applications_deb (g, fs);
90 case OS_PACKAGE_FORMAT_PACMAN:
91 case OS_PACKAGE_FORMAT_EBUILD:
92 case OS_PACKAGE_FORMAT_PISI:
93 case OS_PACKAGE_FORMAT_PKGSRC:
94 case OS_PACKAGE_FORMAT_UNKNOWN:
96 /* nothing - keep GCC happy */;
100 case OS_TYPE_WINDOWS:
101 ret = list_applications_windows (g, fs);
106 case OS_TYPE_FREEBSD:
108 case OS_TYPE_UNKNOWN:
110 /* nothing - keep GCC happy */;
115 /* Don't know how to do inspection. Not an error, return an
118 ret = safe_malloc (g, sizeof *ret);
123 sort_applications (ret);
130 /* This data comes from the Name database, and contains the application
131 * names and the first 4 bytes of the link field.
133 struct rpm_names_list {
134 struct rpm_name *names;
143 free_rpm_names_list (struct rpm_names_list *list)
147 for (i = 0; i < list->len; ++i)
148 free (list->names[i].name);
153 compare_links (const void *av, const void *bv)
155 const struct rpm_name *a = av;
156 const struct rpm_name *b = bv;
157 return memcmp (a->link, b->link, 4);
161 read_rpm_name (guestfs_h *g,
162 const unsigned char *key, size_t keylen,
163 const unsigned char *value, size_t valuelen,
166 struct rpm_names_list *list = listv;
169 /* Ignore bogus entries. */
170 if (keylen == 0 || valuelen < 4)
173 /* The name (key) field won't be NUL-terminated, so we must do that. */
174 name = safe_malloc (g, keylen+1);
175 memcpy (name, key, keylen);
178 list->names = safe_realloc (g, list->names,
179 (list->len + 1) * sizeof (struct rpm_name));
180 list->names[list->len].name = name;
181 memcpy (list->names[list->len].link, value, 4);
187 struct read_package_data {
188 struct rpm_names_list *list;
189 struct guestfs_application_list *apps;
193 read_package (guestfs_h *g,
194 const unsigned char *key, size_t keylen,
195 const unsigned char *value, size_t valuelen,
198 struct read_package_data *data = datav;
199 struct rpm_name nkey, *entry;
203 char *nul_name_nul, *version, *release;
205 /* This function reads one (key, value) pair from the Packages
206 * database. The key is the link field (see struct rpm_name). The
207 * value is a long binary string, but we can extract the version
208 * number from it as below. First we have to look up the link field
209 * in the list of links (which is sorted by link field).
212 /* Ignore bogus entries. */
213 if (keylen < 4 || valuelen == 0)
216 /* Look up the link (key) in the list. */
217 memcpy (nkey.link, key, 4);
218 entry = bsearch (&nkey, data->list->names, data->list->len,
219 sizeof (struct rpm_name), compare_links);
221 return 0; /* Not found - ignore it. */
223 /* We found a matching link entry, so that gives us the application
224 * name (entry->name). Now we can get other data for this
225 * application out of the binary value string. XXX This is a real
229 /* Look for \0<name>\0 */
230 len = strlen (entry->name);
231 nul_name_nul = safe_malloc (g, len + 2);
232 nul_name_nul[0] = '\0';
233 memcpy (&nul_name_nul[1], entry->name, len);
234 nul_name_nul[len+1] = '\0';
235 p = memmem (value, valuelen, nul_name_nul, len+2);
240 /* Following that are \0-delimited version and release fields. */
241 p += len + 2; /* Note we have to skip \0 + name + \0. */
242 max = valuelen - (p - (char *) value);
245 version = safe_strndup (g, p, max);
247 len = strlen (version);
249 max = valuelen - (p - (char *) value);
252 release = safe_strndup (g, p, max);
254 /* Add the application and what we know. */
255 add_application (g, data->apps, entry->name, "", 0, version, release,
264 static struct guestfs_application_list *
265 list_applications_rpm (guestfs_h *g, struct inspect_fs *fs)
267 char *Name = NULL, *Packages = NULL;
268 struct rpm_names_list list = { .names = NULL, .len = 0 };
269 struct guestfs_application_list *apps = NULL;
271 Name = guestfs___download_to_tmp (g, fs,
272 "/var/lib/rpm/Name", "rpm_Name",
277 Packages = guestfs___download_to_tmp (g, fs,
278 "/var/lib/rpm/Packages", "rpm_Packages",
280 if (Packages == NULL)
283 /* Read Name database. */
284 if (guestfs___read_db_dump (g, Name, &list, read_rpm_name) == -1)
287 /* Sort the names by link field for fast searching. */
288 qsort (list.names, list.len, sizeof (struct rpm_name), compare_links);
290 /* Allocate 'apps' list. */
291 apps = safe_malloc (g, sizeof *apps);
295 /* Read Packages database. */
296 struct read_package_data data = { .list = &list, .apps = apps };
297 if (guestfs___read_db_dump (g, Packages, &data, read_package) == -1)
302 free_rpm_names_list (&list);
309 free_rpm_names_list (&list);
311 guestfs_free_application_list (apps);
316 #endif /* defined DB_DUMP */
318 static struct guestfs_application_list *
319 list_applications_deb (guestfs_h *g, struct inspect_fs *fs)
322 status = guestfs___download_to_tmp (g, fs, "/var/lib/dpkg/status", "status",
327 struct guestfs_application_list *apps = NULL, *ret = NULL;
331 char *name = NULL, *version = NULL, *release = NULL;
332 int installed_flag = 0;
334 fp = fopen (status, "r");
336 perrorf (g, "fopen: %s", status);
340 /* Allocate 'apps' list. */
341 apps = safe_malloc (g, sizeof *apps);
345 /* Read the temporary file. Each package entry is separated by
347 * XXX Strictly speaking this is in mailbox header format, so it
348 * would be possible for fields to spread across multiple lines,
349 * although for the short fields that we are concerned about this is
350 * unlikely and not seen in practice.
352 while (fgets (line, sizeof line, fp) != NULL) {
354 if (len > 0 && line[len-1] == '\n') {
359 if (STRPREFIX (line, "Package: ")) {
361 name = safe_strdup (g, &line[9]);
363 else if (STRPREFIX (line, "Status: ")) {
364 installed_flag = strstr (&line[8], "installed") != NULL;
366 else if (STRPREFIX (line, "Version: ")) {
369 char *p = strchr (&line[9], '-');
372 version = safe_strdup (g, &line[9]);
373 release = safe_strdup (g, p+1);
375 version = safe_strdup (g, &line[9]);
379 else if (STREQ (line, "")) {
380 if (installed_flag && name && version)
381 add_application (g, apps, name, "", 0, version, release ? : "",
386 name = version = release = NULL;
391 if (fclose (fp) == -1) {
392 perrorf (g, "fclose: %s", status);
400 if (ret == NULL && apps != NULL)
401 guestfs_free_application_list (apps);
411 static void list_applications_windows_from_path (guestfs_h *g, hive_h *h, struct guestfs_application_list *apps, const char **path, size_t path_len);
413 static struct guestfs_application_list *
414 list_applications_windows (guestfs_h *g, struct inspect_fs *fs)
416 size_t len = strlen (fs->windows_systemroot) + 64;
418 snprintf (software, len, "%s/system32/config/software",
419 fs->windows_systemroot);
421 char *software_path = guestfs___case_sensitive_path_silently (g, software);
422 if (!software_path) {
423 /* Missing software hive is a problem. */
424 error (g, "no HKLM\\SOFTWARE hive found in the guest");
428 char *software_hive = NULL;
429 struct guestfs_application_list *ret = NULL;
432 software_hive = guestfs___download_to_tmp (g, fs, software_path, "software",
434 if (software_hive == NULL)
437 free (software_path);
438 software_path = NULL;
440 h = hivex_open (software_hive, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
442 perrorf (g, "hivex_open");
446 /* Allocate apps list. */
447 ret = safe_malloc (g, sizeof *ret);
451 /* Ordinary native applications. */
452 const char *hivepath[] =
453 { "Microsoft", "Windows", "CurrentVersion", "Uninstall" };
454 list_applications_windows_from_path (g, h, ret, hivepath,
455 sizeof hivepath / sizeof hivepath[0]);
457 /* 32-bit emulated Windows apps running on the WOW64 emulator.
458 * http://support.microsoft.com/kb/896459 (RHBZ#692545).
460 const char *hivepath2[] =
461 { "WOW6432node", "Microsoft", "Windows", "CurrentVersion", "Uninstall" };
462 list_applications_windows_from_path (g, h, ret, hivepath2,
463 sizeof hivepath2 / sizeof hivepath2[0]);
466 if (h) hivex_close (h);
467 free (software_path);
468 free (software_hive);
474 list_applications_windows_from_path (guestfs_h *g, hive_h *h,
475 struct guestfs_application_list *apps,
476 const char **path, size_t path_len)
478 hive_node_h *children = NULL;
482 node = hivex_root (h);
484 for (i = 0; node != 0 && i < path_len; ++i)
485 node = hivex_node_get_child (h, node, path[i]);
490 children = hivex_node_children (h, node);
491 if (children == NULL)
494 /* Consider any child node that has a DisplayName key.
496 * http://nsis.sourceforge.net/Add_uninstall_information_to_Add/Remove_Programs#Optional_values
498 for (i = 0; children[i] != 0; ++i) {
501 char *display_name = NULL;
502 char *version = NULL;
503 char *install_path = NULL;
504 char *publisher = NULL;
506 char *comments = NULL;
508 /* Use the node name as a proxy for the package name in Linux. The
509 * display name is not language-independent, so it cannot be used.
511 name = hivex_node_name (h, children[i]);
515 value = hivex_node_get_value (h, children[i], "DisplayName");
517 display_name = hivex_value_string (h, value);
519 value = hivex_node_get_value (h, children[i], "DisplayVersion");
521 version = hivex_value_string (h, value);
522 value = hivex_node_get_value (h, children[i], "InstallLocation");
524 install_path = hivex_value_string (h, value);
525 value = hivex_node_get_value (h, children[i], "Publisher");
527 publisher = hivex_value_string (h, value);
528 value = hivex_node_get_value (h, children[i], "URLInfoAbout");
530 url = hivex_value_string (h, value);
531 value = hivex_node_get_value (h, children[i], "Comments");
533 comments = hivex_value_string (h, value);
535 add_application (g, apps, name, display_name, 0,
558 add_application (guestfs_h *g, struct guestfs_application_list *apps,
559 const char *name, const char *display_name, int32_t epoch,
560 const char *version, const char *release,
561 const char *install_path,
562 const char *publisher, const char *url,
563 const char *description)
566 apps->val = safe_realloc (g, apps->val,
567 apps->len * sizeof (struct guestfs_application));
568 apps->val[apps->len-1].app_name = safe_strdup (g, name);
569 apps->val[apps->len-1].app_display_name = safe_strdup (g, display_name);
570 apps->val[apps->len-1].app_epoch = epoch;
571 apps->val[apps->len-1].app_version = safe_strdup (g, version);
572 apps->val[apps->len-1].app_release = safe_strdup (g, release);
573 apps->val[apps->len-1].app_install_path = safe_strdup (g, install_path);
574 /* XXX Translated path is not implemented yet. */
575 apps->val[apps->len-1].app_trans_path = safe_strdup (g, "");
576 apps->val[apps->len-1].app_publisher = safe_strdup (g, publisher);
577 apps->val[apps->len-1].app_url = safe_strdup (g, url);
578 /* XXX The next two are not yet implemented for any package
579 * format, but we could easily support them for rpm and deb.
581 apps->val[apps->len-1].app_source_package = safe_strdup (g, "");
582 apps->val[apps->len-1].app_summary = safe_strdup (g, "");
583 apps->val[apps->len-1].app_description = safe_strdup (g, description);
586 /* Sort applications by name before returning the list. */
588 compare_applications (const void *vp1, const void *vp2)
590 const struct guestfs_application *v1 = vp1;
591 const struct guestfs_application *v2 = vp2;
593 return strcmp (v1->app_name, v2->app_name);
597 sort_applications (struct guestfs_application_list *apps)
599 if (apps && apps->val)
600 qsort (apps->val, apps->len, sizeof (struct guestfs_application),
601 compare_applications);
604 #else /* no hivex at compile time */
606 /* XXX These functions should be in an optgroup. */
608 #define NOT_IMPL(r) \
609 error (g, _("inspection API not available since this version of libguestfs was compiled without the hivex library")); \
612 struct guestfs_application_list *
613 guestfs__inspect_list_applications (guestfs_h *g, const char *root)
618 #endif /* no hivex at compile time */