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

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


Convert the Perl code to C.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
virt-p2v converts physical machines to virtual machines.  Boot with a
live CD or over the network (PXE) and turn machines into Xen guests.
http://et.redhat.com/~rjones/virt-p2v
-------------- next part --------------
>From dda8950ab2e2450fb33a7164fbdbca270c4eb5f8 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.
---
 generator/generator_actions.ml                 |   54 +++++
 generator/generator_structs.ml                 |    9 +
 java/Makefile.inc                              |    1 +
 java/com/redhat/et/libguestfs/Application.java |   36 +++
 src/guestfs.pod                                |    4 +-
 src/inspect.c                                  |  279 ++++++++++++++++++++++++
 6 files changed, 382 insertions(+), 1 deletions(-)
 create mode 100644 java/com/redhat/et/libguestfs/Application.java

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/java/com/redhat/et/libguestfs/Application.java b/java/com/redhat/et/libguestfs/Application.java
new file mode 100644
index 0000000..dae3929
--- /dev/null
+++ b/java/com/redhat/et/libguestfs/Application.java
@@ -0,0 +1,36 @@
+/* libguestfs generated file
+ * WARNING: THIS FILE IS GENERATED FROM:
+ *   generator/generator_*.ml
+ * ANY CHANGES YOU MAKE TO THIS FILE WILL BE LOST.
+ *
+ * Copyright (C) 2009-2010 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
+ */
+
+package com.redhat.et.libguestfs;
+
+/**
+ * Libguestfs Application structure.
+ *
+ * @author rjones
+ * @see GuestFS
+ */
+public class Application {
+  public String app_name;
+  public int app_epoch;
+  public String app_version;
+  public String app_release;
+}
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..f5d6dda 100644
--- a/src/inspect.c
+++ b/src/inspect.c
@@ -1424,6 +1424,279 @@ 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 +1828,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