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
41 #include "ignore-value.h"
45 #include "guestfs-internal.h"
46 #include "guestfs-internal-actions.h"
47 #include "guestfs_protocol.h"
49 #if defined(HAVE_PCRE) && defined(HAVE_HIVEX)
52 static struct guestfs_application_list *list_applications_rpm (guestfs_h *g, struct inspect_fs *fs);
54 static struct guestfs_application_list *list_applications_deb (guestfs_h *g, struct inspect_fs *fs);
55 static struct guestfs_application_list *list_applications_windows (guestfs_h *g, struct inspect_fs *fs);
56 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);
57 static void sort_applications (struct guestfs_application_list *);
59 /* Unlike the simple inspect-get-* calls, this one assumes that the
60 * disks are mounted up, and reads files from the mounted disks.
62 struct guestfs_application_list *
63 guestfs__inspect_list_applications (guestfs_h *g, const char *root)
65 struct inspect_fs *fs = guestfs___search_for_root (g, root);
69 struct guestfs_application_list *ret = NULL;
71 /* Presently we can only list applications for installed disks. It
72 * is possible in future to get lists of packages from installers.
74 if (fs->format == OS_FORMAT_INSTALLED) {
77 switch (fs->package_format) {
78 case OS_PACKAGE_FORMAT_RPM:
80 ret = list_applications_rpm (g, fs);
86 case OS_PACKAGE_FORMAT_DEB:
87 ret = list_applications_deb (g, fs);
92 case OS_PACKAGE_FORMAT_PACMAN:
93 case OS_PACKAGE_FORMAT_EBUILD:
94 case OS_PACKAGE_FORMAT_PISI:
95 case OS_PACKAGE_FORMAT_UNKNOWN:
97 /* nothing - keep GCC happy */;
101 case OS_TYPE_WINDOWS:
102 ret = list_applications_windows (g, fs);
107 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);
129 static struct guestfs_application_list *
130 list_applications_rpm (guestfs_h *g, struct inspect_fs *fs)
132 const char *basename = "rpm_Name";
133 char tmpdir_basename[strlen (g->tmpdir) + strlen (basename) + 2];
134 snprintf (tmpdir_basename, sizeof tmpdir_basename, "%s/%s",
135 g->tmpdir, basename);
137 if (guestfs___download_to_tmp (g, "/var/lib/rpm/Name", basename,
138 MAX_PKG_DB_SIZE) == -1)
141 struct guestfs_application_list *apps = NULL, *ret = NULL;
142 #define cmd_len (strlen (tmpdir_basename) + 64)
148 snprintf (cmd, cmd_len, DB_DUMP " -p '%s'", tmpdir_basename);
150 debug (g, "list_applications_rpm: %s", cmd);
152 pp = popen (cmd, "r");
154 perrorf (g, "popen: %s", cmd);
158 /* Ignore everything to end-of-header marker. */
160 if (fgets (line, sizeof line, pp) == NULL) {
161 error (g, _("unexpected end of output from db_dump command"));
166 if (len > 0 && line[len-1] == '\n') {
171 if (STREQ (line, "HEADER=END"))
175 /* Allocate 'apps' list. */
176 apps = safe_malloc (g, sizeof *apps);
180 /* Read alternate lines until end of data marker. */
182 if (fgets (line, sizeof line, pp) == NULL) {
183 error (g, _("unexpected end of output from db_dump command"));
188 if (len > 0 && line[len-1] == '\n') {
193 if (STREQ (line, "DATA=END"))
197 if (len > 0 && line[0] == ' ')
199 /* Ignore any application name that contains non-printable chars.
200 * In the db_dump output these would be escaped with backslash, so
201 * we can just ignore any such line.
203 if (strchr (p, '\\') == NULL)
204 add_application (g, apps, p, "", 0, "", "", "", "", "", "");
206 /* Discard next line. */
207 if (fgets (line, sizeof line, pp) == NULL) {
208 error (g, _("unexpected end of output from db_dump command"));
213 /* Catch errors from the db_dump command. */
214 if (pclose (pp) == -1) {
215 perrorf (g, "pclose: %s", cmd);
223 if (ret == NULL && apps != NULL)
224 guestfs_free_application_list (apps);
230 #endif /* defined DB_DUMP */
232 static struct guestfs_application_list *
233 list_applications_deb (guestfs_h *g, struct inspect_fs *fs)
235 const char *basename = "deb_status";
236 char tmpdir_basename[strlen (g->tmpdir) + strlen (basename) + 2];
237 snprintf (tmpdir_basename, sizeof tmpdir_basename, "%s/%s",
238 g->tmpdir, basename);
240 if (guestfs___download_to_tmp (g, "/var/lib/dpkg/status", basename,
241 MAX_PKG_DB_SIZE) == -1)
244 struct guestfs_application_list *apps = NULL, *ret = NULL;
248 char *name = NULL, *version = NULL, *release = NULL;
249 int installed_flag = 0;
251 fp = fopen (tmpdir_basename, "r");
253 perrorf (g, "fopen: %s", tmpdir_basename);
257 /* Allocate 'apps' list. */
258 apps = safe_malloc (g, sizeof *apps);
262 /* Read the temporary file. Each package entry is separated by
264 * XXX Strictly speaking this is in mailbox header format, so it
265 * would be possible for fields to spread across multiple lines,
266 * although for the short fields that we are concerned about this is
267 * unlikely and not seen in practice.
269 while (fgets (line, sizeof line, fp) != NULL) {
271 if (len > 0 && line[len-1] == '\n') {
276 if (STRPREFIX (line, "Package: ")) {
278 name = safe_strdup (g, &line[9]);
280 else if (STRPREFIX (line, "Status: ")) {
281 installed_flag = strstr (&line[8], "installed") != NULL;
283 else if (STRPREFIX (line, "Version: ")) {
286 char *p = strchr (&line[9], '-');
289 version = safe_strdup (g, &line[9]);
290 release = safe_strdup (g, p+1);
292 version = safe_strdup (g, &line[9]);
296 else if (STREQ (line, "")) {
297 if (installed_flag && name && version)
298 add_application (g, apps, name, "", 0, version, release ? : "",
303 name = version = release = NULL;
308 if (fclose (fp) == -1) {
309 perrorf (g, "fclose: %s", tmpdir_basename);
317 if (ret == NULL && apps != NULL)
318 guestfs_free_application_list (apps);
327 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);
329 static struct guestfs_application_list *
330 list_applications_windows (guestfs_h *g, struct inspect_fs *fs)
332 const char *basename = "software";
333 char tmpdir_basename[strlen (g->tmpdir) + strlen (basename) + 2];
334 snprintf (tmpdir_basename, sizeof tmpdir_basename, "%s/%s",
335 g->tmpdir, basename);
337 size_t len = strlen (fs->windows_systemroot) + 64;
339 snprintf (software, len, "%s/system32/config/software",
340 fs->windows_systemroot);
342 char *software_path = guestfs___case_sensitive_path_silently (g, software);
344 /* If the software hive doesn't exist, just accept that we cannot
349 struct guestfs_application_list *ret = NULL;
352 if (guestfs___download_to_tmp (g, software_path, basename,
353 MAX_REGISTRY_SIZE) == -1)
356 free (software_path);
357 software_path = NULL;
359 h = hivex_open (tmpdir_basename, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
361 perrorf (g, "hivex_open");
365 /* Allocate apps list. */
366 ret = safe_malloc (g, sizeof *ret);
370 /* Ordinary native applications. */
371 const char *hivepath[] =
372 { "Microsoft", "Windows", "CurrentVersion", "Uninstall" };
373 list_applications_windows_from_path (g, h, ret, hivepath,
374 sizeof hivepath / sizeof hivepath[0]);
376 /* 32-bit emulated Windows apps running on the WOW64 emulator.
377 * http://support.microsoft.com/kb/896459 (RHBZ#692545).
379 const char *hivepath2[] =
380 { "WOW6432node", "Microsoft", "Windows", "CurrentVersion", "Uninstall" };
381 list_applications_windows_from_path (g, h, ret, hivepath2,
382 sizeof hivepath2 / sizeof hivepath2[0]);
385 if (h) hivex_close (h);
386 free (software_path);
392 list_applications_windows_from_path (guestfs_h *g, hive_h *h,
393 struct guestfs_application_list *apps,
394 const char **path, size_t path_len)
396 hive_node_h *children = NULL;
400 node = hivex_root (h);
402 for (i = 0; node != 0 && i < path_len; ++i)
403 node = hivex_node_get_child (h, node, path[i]);
408 children = hivex_node_children (h, node);
409 if (children == NULL)
412 /* Consider any child node that has a DisplayName key.
414 * http://nsis.sourceforge.net/Add_uninstall_information_to_Add/Remove_Programs#Optional_values
416 for (i = 0; children[i] != 0; ++i) {
419 char *display_name = NULL;
420 char *version = NULL;
421 char *install_path = NULL;
422 char *publisher = NULL;
424 char *comments = NULL;
426 /* Use the node name as a proxy for the package name in Linux. The
427 * display name is not language-independent, so it cannot be used.
429 name = hivex_node_name (h, children[i]);
433 value = hivex_node_get_value (h, children[i], "DisplayName");
435 display_name = hivex_value_string (h, value);
437 value = hivex_node_get_value (h, children[i], "DisplayVersion");
439 version = hivex_value_string (h, value);
440 value = hivex_node_get_value (h, children[i], "InstallLocation");
442 install_path = hivex_value_string (h, value);
443 value = hivex_node_get_value (h, children[i], "Publisher");
445 publisher = hivex_value_string (h, value);
446 value = hivex_node_get_value (h, children[i], "URLInfoAbout");
448 url = hivex_value_string (h, value);
449 value = hivex_node_get_value (h, children[i], "Comments");
451 comments = hivex_value_string (h, value);
453 add_application (g, apps, name, display_name, 0,
476 add_application (guestfs_h *g, struct guestfs_application_list *apps,
477 const char *name, const char *display_name, int32_t epoch,
478 const char *version, const char *release,
479 const char *install_path,
480 const char *publisher, const char *url,
481 const char *description)
484 apps->val = safe_realloc (g, apps->val,
485 apps->len * sizeof (struct guestfs_application));
486 apps->val[apps->len-1].app_name = safe_strdup (g, name);
487 apps->val[apps->len-1].app_display_name = safe_strdup (g, display_name);
488 apps->val[apps->len-1].app_epoch = epoch;
489 apps->val[apps->len-1].app_version = safe_strdup (g, version);
490 apps->val[apps->len-1].app_release = safe_strdup (g, release);
491 apps->val[apps->len-1].app_install_path = safe_strdup (g, install_path);
492 /* XXX Translated path is not implemented yet. */
493 apps->val[apps->len-1].app_trans_path = safe_strdup (g, "");
494 apps->val[apps->len-1].app_publisher = safe_strdup (g, publisher);
495 apps->val[apps->len-1].app_url = safe_strdup (g, url);
496 /* XXX The next two are not yet implemented for any package
497 * format, but we could easily support them for rpm and deb.
499 apps->val[apps->len-1].app_source_package = safe_strdup (g, "");
500 apps->val[apps->len-1].app_summary = safe_strdup (g, "");
501 apps->val[apps->len-1].app_description = safe_strdup (g, description);
504 /* Sort applications by name before returning the list. */
506 compare_applications (const void *vp1, const void *vp2)
508 const struct guestfs_application *v1 = vp1;
509 const struct guestfs_application *v2 = vp2;
511 return strcmp (v1->app_name, v2->app_name);
515 sort_applications (struct guestfs_application_list *apps)
517 if (apps && apps->val)
518 qsort (apps->val, apps->len, sizeof (struct guestfs_application),
519 compare_applications);
522 #else /* no PCRE or hivex at compile time */
524 /* XXX These functions should be in an optgroup. */
526 #define NOT_IMPL(r) \
527 error (g, _("inspection API not available since this version of libguestfs was compiled without PCRE or hivex libraries")); \
530 struct guestfs_application_list *
531 guestfs__inspect_list_applications (guestfs_h *g, const char *root)
536 #endif /* no PCRE or hivex at compile time */