[Libguestfs] [PATCH 2/3 version 2] New API: inspect-list-applications.

Richard W.M. Jones rjones at redhat.com
Mon Nov 15 14:29:35 UTC 2010


This updated patch fixes a couple of problems with 2/3.

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
New in Fedora 11: Fedora Windows cross-compiler. Compile Windows
programs, test, and build Windows installers. Over 70 libraries supprt'd
http://fedoraproject.org/wiki/MinGW http://www.annexia.org/fedora_mingw
-------------- next part --------------
>From 2f227200e992c1f53ad2ec520af4d28f4520d9b6 Mon Sep 17 00:00:00 2001
From: Richard W.M. Jones <rjones at redhat.com>
Date: Mon, 15 Nov 2010 12:26:36 +0000
Subject: [PATCH 2/3] New API: inspect-list-applications.

This converts the current Perl code in virt-inspector for
listing applications, into C, making it a part of the core API.
---
 .gitignore                     |    1 +
 generator/generator_actions.ml |   54 ++++++++
 generator/generator_structs.ml |    9 ++
 java/Makefile.inc              |    1 +
 src/guestfs.pod                |    4 +-
 src/inspect.c                  |  278 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 346 insertions(+), 1 deletions(-)

diff --git a/.gitignore b/.gitignore
index be792c9..b4cc493 100644
--- a/.gitignore
+++ b/.gitignore
@@ -140,6 +140,7 @@ install-sh
 *.jar
 java/api
 java/Bindtests.java
+java/com/redhat/et/libguestfs/Application.java
 java/com/redhat/et/libguestfs/Dirent.java
 java/com_redhat_et_libguestfs_GuestFS.c
 java/com_redhat_et_libguestfs_GuestFS.h
diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml
index c5bd44b..b6efb61 100644
--- a/generator/generator_actions.ml
+++ b/generator/generator_actions.ml
@@ -1169,6 +1169,60 @@ Future versions of libguestfs may return other strings.
 
 Please read L<guestfs(3)/INSPECTION> for more details.");
 
+  ("inspect_list_applications", (RStructList ("applications", "application"), [Device "root"], []), -1, [],
+   [],
+   "get list of applications installed in the operating system",
+   "\
+This function should only be called with a root device string
+as returned by C<guestfs_inspect_os>.
+
+Return the list of applications installed in the operating system.
+
+I<Note:> This call works differently from other parts of the
+inspection API.  You have to call C<guestfs_inspect_os>, then
+C<guestfs_inspect_get_mountpoints>, then mount up the disks,
+before calling this.  Listing applications is a significantly
+more difficult operation which requires access to the full
+filesystem.  Also note that unlike the other
+C<guestfs_inspect_get_*> calls which are just returning
+data cached in the libguestfs handle, this call actually reads
+parts of the mounted filesystems during the call.
+
+This returns an empty list if the inspection code was not able
+to determine the list of applications.  Support for Windows guests
+is not implemented yet, so for Windows guests currently this function
+always returns an empty list.  We expect to support Windows in
+a future version of libguestfs.
+
+The application structure contains the following fields:
+
+=over 4
+
+=item C<app_name>
+
+The name of the application.  For Red Hat-derived and Debian-derived
+Linux guests, this is the package name.
+
+=item C<app_epoch>
+
+For package managers which use epochs, this contains the epoch of
+the package (an integer).  If unavailable, this returns C<0>.
+
+=item C<app_version>
+
+The version string of the application or package.  If unavailable
+this is returned as an empty string C<\"\">.
+
+=item C<app_release>
+
+The release string of the application or package, for package
+managers that use this.  If unavailable this is returned as an
+empty string C<\"\">.
+
+=back
+
+Please read L<guestfs(3)/INSPECTION> for more details.");
+
 ]
 
 (* daemon_functions are any functions which cause some action
diff --git a/generator/generator_structs.ml b/generator/generator_structs.ml
index 9cd585b..2dd4e99 100644
--- a/generator/generator_structs.ml
+++ b/generator/generator_structs.ml
@@ -175,6 +175,14 @@ let structs = [
     "part_end", FBytes;
     "part_size", FBytes;
   ];
+
+  (* Application. *)
+  "application", [
+    "app_name", FString;
+    "app_epoch", FInt32;
+    "app_version", FString;
+    "app_release", FString;
+  ];
 ] (* end of structs *)
 
 (* Ugh, Java has to be different ..
@@ -192,6 +200,7 @@ let java_structs = [
   "xattr", "XAttr";
   "inotify_event", "INotifyEvent";
   "partition", "Partition";
+  "application", "Application";
 ]
 
 let java_name_of_struct typ =
diff --git a/java/Makefile.inc b/java/Makefile.inc
index 88550ab..545f555 100644
--- a/java/Makefile.inc
+++ b/java/Makefile.inc
@@ -31,4 +31,5 @@ java_built_sources = \
 	com/redhat/et/libguestfs/XAttr.java \
 	com/redhat/et/libguestfs/INotifyEvent.java \
 	com/redhat/et/libguestfs/Partition.java \
+	com/redhat/et/libguestfs/Application.java \
 	com/redhat/et/libguestfs/GuestFS.java
diff --git a/src/guestfs.pod b/src/guestfs.pod
index b50f608..8370982 100644
--- a/src/guestfs.pod
+++ b/src/guestfs.pod
@@ -576,7 +576,9 @@ inspection and caches the results in the guest handle.  Subsequent
 calls to C<guestfs_inspect_get_*> return this cached information, but
 I<do not> re-read the disks.  If you change the content of the guest
 disks, you can redo inspection by calling L</guestfs_inspect_os>
-again.
+again.  (L</guestfs_inspect_list_applications> works a little
+differently from the other calls and does read the disks.  See
+documentation for that function for details).
 
 =head2 SPECIAL CONSIDERATIONS FOR WINDOWS GUESTS
 
diff --git a/src/inspect.c b/src/inspect.c
index 4533746..8bbe61a 100644
--- a/src/inspect.c
+++ b/src/inspect.c
@@ -1424,6 +1424,278 @@ guestfs__inspect_get_package_management (guestfs_h *g, const char *root)
   return ret;
 }
 
+static struct guestfs_application_list *list_applications_rpm (guestfs_h *g, struct inspect_fs *fs);
+static struct guestfs_application_list *list_applications_deb (guestfs_h *g, struct inspect_fs *fs);
+static void add_application (guestfs_h *g, struct guestfs_application_list *, const char *name, int32_t epoch, const char *version, const char *release);
+static void sort_applications (struct guestfs_application_list *);
+
+/* Unlike the simple inspect-get-* calls, this one assumes that the
+ * disks are mounted up, and reads files from the mounted disks.
+ */
+struct guestfs_application_list *
+guestfs__inspect_list_applications (guestfs_h *g, const char *root)
+{
+  struct inspect_fs *fs = search_for_root (g, root);
+  if (!fs)
+    return NULL;
+
+  struct guestfs_application_list *ret = NULL;
+
+  switch (fs->package_format) {
+  case OS_PACKAGE_FORMAT_RPM:
+    ret = list_applications_rpm (g, fs);
+    if (ret == NULL)
+      return NULL;
+    break;
+
+  case OS_PACKAGE_FORMAT_DEB:
+    ret = list_applications_deb (g, fs);
+    if (ret == NULL)
+      return NULL;
+    break;
+
+  case OS_PACKAGE_FORMAT_UNKNOWN:
+  case OS_PACKAGE_FORMAT_PACMAN:
+  case OS_PACKAGE_FORMAT_EBUILD:
+  case OS_PACKAGE_FORMAT_PISI:
+  default:
+    /* nothing - keep GCC happy */;
+  }
+
+  if (ret == NULL) {
+    /* Return an empty list. */
+    ret = safe_malloc (g, sizeof *ret);
+    ret->len = 0;
+    ret->val = NULL;
+  }
+
+  sort_applications (ret);
+
+  return ret;
+}
+
+static struct guestfs_application_list *
+list_applications_rpm (guestfs_h *g, struct inspect_fs *fs)
+{
+  TMP_TEMPLATE_ON_STACK (tmpfile);
+
+  if (download_to_tmp (g, "/var/lib/rpm/Name", tmpfile, 10000000) == -1)
+    return NULL;
+
+  struct guestfs_application_list *apps = NULL, *ret = NULL;
+#define cmd_len (strlen (tmpfile) + 64)
+  char cmd[cmd_len];
+  FILE *pp = NULL;
+  char line[1024];
+  size_t len;
+
+  snprintf (cmd, cmd_len, "db_dump -p '%s'", tmpfile);
+
+  if (g->verbose)
+    fprintf (stderr, "list_applications_rpm: %s\n", cmd);
+
+  pp = popen (cmd, "r");
+  if (pp == NULL) {
+    perrorf (g, "popen: %s", cmd);
+    goto out;
+  }
+
+  /* Ignore everything to end-of-header marker. */
+  for (;;) {
+    if (fgets (line, sizeof line, pp) == NULL) {
+      error (g, _("unexpected end of output from db_dump command"));
+      goto out;
+    }
+
+    len = strlen (line);
+    if (len > 0 && line[len-1] == '\n') {
+      line[len-1] = '\0';
+      len--;
+    }
+
+    if (STREQ (line, "HEADER=END"))
+      break;
+  }
+
+  /* Allocate 'apps' list. */
+  apps = safe_malloc (g, sizeof *apps);
+  apps->len = 0;
+  apps->val = NULL;
+
+  /* Read alternate lines until end of data marker. */
+  for (;;) {
+    if (fgets (line, sizeof line, pp) == NULL) {
+      error (g, _("unexpected end of output from db_dump command"));
+      goto out;
+    }
+
+    len = strlen (line);
+    if (len > 0 && line[len-1] == '\n') {
+      line[len-1] = '\0';
+      len--;
+    }
+
+    if (STREQ (line, "DATA=END"))
+      break;
+
+    char *p = line;
+    if (len > 0 && line[0] == ' ')
+      p = line+1;
+    /* Ignore any application name that contains non-printable chars.
+     * In the db_dump output these would be escaped with backslash, so
+     * we can just ignore any such line.
+     */
+    if (strchr (p, '\\') == NULL)
+      add_application (g, apps, p, 0, "", "");
+
+    /* Discard next line. */
+    if (fgets (line, sizeof line, pp) == NULL) {
+      error (g, _("unexpected end of output from db_dump command"));
+      goto out;
+    }
+  }
+
+  /* Catch errors from the db_dump command. */
+  if (pclose (pp) == -1) {
+    perrorf (g, "pclose: %s", cmd);
+    goto out;
+  }
+  pp = NULL;
+
+  ret = apps;
+
+ out:
+  if (ret == NULL && apps != NULL)
+    guestfs_free_application_list (apps);
+  if (pp)
+    pclose (pp);
+  unlink (tmpfile);
+#undef cmd_len
+
+  return ret;
+}
+
+static struct guestfs_application_list *
+list_applications_deb (guestfs_h *g, struct inspect_fs *fs)
+{
+  TMP_TEMPLATE_ON_STACK (tmpfile);
+
+  if (download_to_tmp (g, "/var/lib/dpkg/status", tmpfile, 10000000) == -1)
+    return NULL;
+
+  struct guestfs_application_list *apps = NULL, *ret = NULL;
+  FILE *fp = NULL;
+  char line[1024];
+  size_t len;
+  char *name = NULL, *version = NULL, *release = NULL;
+  int installed_flag = 0;
+
+  fp = fopen (tmpfile, "r");
+  if (fp == NULL) {
+    perrorf (g, "fopen: %s", tmpfile);
+    goto out;
+  }
+
+  /* Allocate 'apps' list. */
+  apps = safe_malloc (g, sizeof *apps);
+  apps->len = 0;
+  apps->val = NULL;
+
+  /* Read the temporary file.  Each package entry is separated by
+   * a blank line.
+   * XXX Strictly speaking this is in mailbox header format, so it
+   * would be possible for fields to spread across multiple lines,
+   * although for the short fields that we are concerned about this is
+   * unlikely and not seen in practice.
+   */
+  while (fgets (line, sizeof line, fp) != NULL) {
+    len = strlen (line);
+    if (len > 0 && line[len-1] == '\n') {
+      line[len-1] = '\0';
+      len--;
+    }
+
+    if (STRPREFIX (line, "Package: ")) {
+      free (name);
+      name = safe_strdup (g, &line[9]);
+    }
+    else if (STRPREFIX (line, "Status: ")) {
+      installed_flag = strstr (&line[8], "installed") != NULL;
+    }
+    else if (STRPREFIX (line, "Version: ")) {
+      free (version);
+      free (release);
+      char *p = strchr (&line[9], '-');
+      if (p) {
+        *p = '\0';
+        version = safe_strdup (g, &line[9]);
+        release = safe_strdup (g, p+1);
+      } else {
+        version = safe_strdup (g, &line[9]);
+        release = NULL;
+      }
+    }
+    else if (STREQ (line, "")) {
+      if (installed_flag && name && version)
+        add_application (g, apps, name, 0, version, release ? : "");
+      free (name);
+      free (version);
+      free (release);
+      name = version = release = NULL;
+      installed_flag = 0;
+    }
+  }
+
+  if (fclose (fp) == -1) {
+    perrorf (g, "fclose: %s", tmpfile);
+    goto out;
+  }
+  fp = NULL;
+
+  ret = apps;
+
+ out:
+  if (fp)
+    fclose (fp);
+  free (name);
+  free (version);
+  free (release);
+  unlink (tmpfile);
+  return ret;
+}
+
+static void
+add_application (guestfs_h *g, struct guestfs_application_list *apps,
+                 const char *name, int32_t epoch,
+                 const char *version, const char *release)
+{
+  apps->len++;
+  apps->val = safe_realloc (g, apps->val,
+                            apps->len * sizeof (struct guestfs_application));
+  apps->val[apps->len-1].app_name = safe_strdup (g, name);
+  apps->val[apps->len-1].app_epoch = epoch;
+  apps->val[apps->len-1].app_version = safe_strdup (g, version);
+  apps->val[apps->len-1].app_release = safe_strdup (g, release);
+}
+
+/* Sort applications by name before returning the list. */
+static int
+compare_applications (const void *vp1, const void *vp2)
+{
+  const struct guestfs_application *v1 = vp1;
+  const struct guestfs_application *v2 = vp2;
+
+  return strcmp (v1->app_name, v2->app_name);
+}
+
+static void
+sort_applications (struct guestfs_application_list *apps)
+{
+  if (apps && apps->val)
+    qsort (apps->val, apps->len, sizeof (struct guestfs_application),
+           compare_applications);
+}
+
 /* Download to a guest file to a local temporary file.  Refuse to
  * download the guest file if it is larger than max_size.  The caller
  * is responsible for deleting the temporary file after use.
@@ -1555,6 +1827,12 @@ guestfs__inspect_get_package_management (guestfs_h *g, const char *root)
   NOT_IMPL(NULL);
 }
 
+struct guestfs_application_list *
+guestfs__inspect_list_applications (guestfs_h *g, const char *root)
+{
+  NOT_IMPL(NULL);
+}
+
 #endif /* no PCRE or hivex at compile time */
 
 void
-- 
1.7.3.2



More information about the Libguestfs mailing list