[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