summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard W.M. Jones <rjones@redhat.com>2011-04-14 17:53:48 +0100
committerRichard W.M. Jones <rjones@redhat.com>2011-04-14 18:19:42 +0100
commitd95874db3dc6c415061b86275d03770b4f28ffbb (patch)
tree07df251ecf41868dea4e897ba26ea55b0a1f251a
parenta986e8dadb0c70634f6d1d89dd3e7bb5d9af3078 (diff)
downloadlibguestfs-d95874db3dc6c415061b86275d03770b4f28ffbb.tar.gz
libguestfs-d95874db3dc6c415061b86275d03770b4f28ffbb.tar.xz
libguestfs-d95874db3dc6c415061b86275d03770b4f28ffbb.zip
inspect: Get version and release of RPM packages.
This commit downloads the Packages RPM database allowing us to find other details about installed RPM packages (via inspect-list-applications). This adds version and release. Epoch cannot yet be found. This commit also updates the Fedora example image so that it contains a dummy RPM Packages database with some data.
-rw-r--r--.gitignore1
-rw-r--r--images/Makefile.am7
-rw-r--r--images/guest-aux/fedora-packages.db.txt13
-rwxr-xr-ximages/guest-aux/make-fedora-img.sh3
-rw-r--r--inspector/example-fedora.xml6
-rw-r--r--src/guestfs-internal.h8
-rw-r--r--src/inspect_apps.c163
7 files changed, 187 insertions, 14 deletions
diff --git a/.gitignore b/.gitignore
index 6b96fb82..06903d4a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -144,6 +144,7 @@ images/abssymlink
images/debian.img
images/fedora.img
images/guest-aux/fedora-name.db
+images/guest-aux/fedora-packages.db
images/hello.b64
images/initrd
images/initrd-x86_64.img
diff --git a/images/Makefile.am b/images/Makefile.am
index d45e6995..97fccce9 100644
--- a/images/Makefile.am
+++ b/images/Makefile.am
@@ -168,7 +168,9 @@ $(builddir)/test-grep.txt.gz: test-grep.txt
mv $@-t $@
# Make a (dummy) Fedora image.
-fedora.img: guest-aux/make-fedora-img.sh guest-aux/fedora-name.db
+fedora.img: guest-aux/make-fedora-img.sh \
+ guest-aux/fedora-name.db \
+ guest-aux/fedora-packages.db
LIBGUESTFS_PATH=../appliance \
LD_LIBRARY_PATH=../src/.libs \
bash $<
@@ -176,6 +178,9 @@ fedora.img: guest-aux/make-fedora-img.sh guest-aux/fedora-name.db
guest-aux/fedora-name.db: guest-aux/fedora-name.db.txt
$(DB_LOAD) $@ < $<
+guest-aux/fedora-packages.db: guest-aux/fedora-packages.db.txt
+ $(DB_LOAD) $@ < $<
+
# Make a (dummy) Debian image.
debian.img: guest-aux/make-debian-img.sh
LIBGUESTFS_PATH=../appliance \
diff --git a/images/guest-aux/fedora-packages.db.txt b/images/guest-aux/fedora-packages.db.txt
new file mode 100644
index 00000000..3939d6bd
--- /dev/null
+++ b/images/guest-aux/fedora-packages.db.txt
@@ -0,0 +1,13 @@
+VERSION=3
+format=print
+type=hash
+h_nelem=3
+db_pagesize=4096
+HEADER=END
+ !\0b\00\00
+ \00test1\001.0\001.fc14\00
+ 7\0b\00\00
+ \00test2\002.0\002.fc14\00
+ \dd\0c\00\00
+ \00test3\003.0\003.fc14\00
+DATA=END
diff --git a/images/guest-aux/make-fedora-img.sh b/images/guest-aux/make-fedora-img.sh
index 8c38e519..8f198f61 100755
--- a/images/guest-aux/make-fedora-img.sh
+++ b/images/guest-aux/make-fedora-img.sh
@@ -1,6 +1,6 @@
#!/bin/bash -
# libguestfs
-# Copyright (C) 2010 Red Hat Inc.
+# Copyright (C) 2010-2011 Red Hat Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -73,6 +73,7 @@ write /etc/fedora-release "Fedora release 14 (Phony)"
write /etc/sysconfig/network "HOSTNAME=fedora.invalid"
upload guest-aux/fedora-name.db /var/lib/rpm/Name
+upload guest-aux/fedora-packages.db /var/lib/rpm/Packages
upload bin-x86_64-dynamic /bin/ls
diff --git a/inspector/example-fedora.xml b/inspector/example-fedora.xml
index 797aa58a..1444bb74 100644
--- a/inspector/example-fedora.xml
+++ b/inspector/example-fedora.xml
@@ -30,12 +30,18 @@
<applications>
<application>
<name>test1</name>
+ <version>1.0</version>
+ <release>1.fc14</release>
</application>
<application>
<name>test2</name>
+ <version>2.0</version>
+ <release>2.fc14</release>
</application>
<application>
<name>test3</name>
+ <version>3.0</version>
+ <release>3.fc14</release>
</application>
</applications>
</operatingsystem>
diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h
index 1730f688..c6d7c871 100644
--- a/src/guestfs-internal.h
+++ b/src/guestfs-internal.h
@@ -82,8 +82,12 @@
*/
#define MAX_REGISTRY_SIZE (100 * 1000 * 1000)
-/* Maximum RPM or dpkg database we will download to /tmp. */
-#define MAX_PKG_DB_SIZE (10 * 1000 * 1000)
+/* Maximum RPM or dpkg database we will download to /tmp. RPM
+ * 'Packages' database can get very large: 70 MB is roughly the
+ * standard size for a new Fedora install, and after lots of package
+ * installation/removal I have seen well over 100 MB databases.
+ */
+#define MAX_PKG_DB_SIZE (300 * 1000 * 1000)
/* Network configuration of the appliance. Note these addresses are
* only meaningful within the context of the running appliance. QEMU
diff --git a/src/inspect_apps.c b/src/inspect_apps.c
index 8ce09ab7..d7634d81 100644
--- a/src/inspect_apps.c
+++ b/src/inspect_apps.c
@@ -127,23 +127,136 @@ guestfs__inspect_list_applications (guestfs_h *g, const char *root)
#ifdef DB_DUMP
+/* This data comes from the Name database, and contains the application
+ * names and the first 4 bytes of the link field.
+ */
+struct rpm_names_list {
+ struct rpm_name *names;
+ size_t len;
+};
+struct rpm_name {
+ char *name;
+ char link[4];
+};
+
+static void
+free_rpm_names_list (struct rpm_names_list *list)
+{
+ size_t i;
+
+ for (i = 0; i < list->len; ++i)
+ free (list->names[i].name);
+ free (list->names);
+}
+
+static int
+compare_links (const void *av, const void *bv)
+{
+ const struct rpm_name *a = av;
+ const struct rpm_name *b = bv;
+ return memcmp (a->link, b->link, 4);
+}
+
static int
read_rpm_name (guestfs_h *g,
const unsigned char *key, size_t keylen,
const unsigned char *value, size_t valuelen,
- void *appsv)
+ void *listv)
{
- struct guestfs_application_list *apps = appsv;
+ struct rpm_names_list *list = listv;
char *name;
+ /* Ignore bogus entries. */
+ if (keylen == 0 || valuelen < 4)
+ return 0;
+
/* The name (key) field won't be NUL-terminated, so we must do that. */
name = safe_malloc (g, keylen+1);
memcpy (name, key, keylen);
name[keylen] = '\0';
- add_application (g, apps, name, "", 0, "", "", "", "", "", "");
+ list->names = safe_realloc (g, list->names,
+ (list->len + 1) * sizeof (struct rpm_name));
+ list->names[list->len].name = name;
+ memcpy (list->names[list->len].link, value, 4);
+ list->len++;
- free (name);
+ return 0;
+}
+
+struct read_package_data {
+ struct rpm_names_list *list;
+ struct guestfs_application_list *apps;
+};
+
+static int
+read_package (guestfs_h *g,
+ const unsigned char *key, size_t keylen,
+ const unsigned char *value, size_t valuelen,
+ void *datav)
+{
+ struct read_package_data *data = datav;
+ struct rpm_name nkey, *entry;
+ char *p;
+ size_t len;
+ ssize_t max;
+ char *nul_name_nul, *version, *release;
+
+ /* This function reads one (key, value) pair from the Packages
+ * database. The key is the link field (see struct rpm_name). The
+ * value is a long binary string, but we can extract the version
+ * number from it as below. First we have to look up the link field
+ * in the list of links (which is sorted by link field).
+ */
+
+ /* Ignore bogus entries. */
+ if (keylen < 4 || valuelen == 0)
+ return 0;
+
+ /* Look up the link (key) in the list. */
+ memcpy (nkey.link, key, 4);
+ entry = bsearch (&nkey, data->list->names, data->list->len,
+ sizeof (struct rpm_name), compare_links);
+ if (!entry)
+ return 0; /* Not found - ignore it. */
+
+ /* We found a matching link entry, so that gives us the application
+ * name (entry->name). Now we can get other data for this
+ * application out of the binary value string. XXX This is a real
+ * hack.
+ */
+
+ /* Look for \0<name>\0 */
+ len = strlen (entry->name);
+ nul_name_nul = safe_malloc (g, len + 2);
+ nul_name_nul[0] = '\0';
+ memcpy (&nul_name_nul[1], entry->name, len);
+ nul_name_nul[len+1] = '\0';
+ p = memmem (value, valuelen, nul_name_nul, len+2);
+ free (nul_name_nul);
+ if (!p)
+ return 0;
+
+ /* Following that are \0-delimited version and release fields. */
+ p += len + 2; /* Note we have to skip \0 + name + \0. */
+ max = valuelen - (p - (char *) value);
+ if (max < 0)
+ max = 0;
+ version = safe_strndup (g, p, max);
+
+ len = strlen (version);
+ p += len + 1;
+ max = valuelen - (p - (char *) value);
+ if (max < 0)
+ max = 0;
+ release = safe_strndup (g, p, max);
+
+ /* Add the application and what we know. */
+ add_application (g, data->apps, entry->name, "", 0, version, release,
+ "", "", "", "");
+
+ free (version);
+ free (release);
return 0;
}
@@ -151,26 +264,56 @@ read_rpm_name (guestfs_h *g,
static struct guestfs_application_list *
list_applications_rpm (guestfs_h *g, struct inspect_fs *fs)
{
- const char *basename = "rpm_Name";
- char tmpdir_basename[strlen (g->tmpdir) + strlen (basename) + 2];
- snprintf (tmpdir_basename, sizeof tmpdir_basename, "%s/%s",
- g->tmpdir, basename);
+ const char *name = "rpm_Name";
+ char tmpdir_name[strlen (g->tmpdir) + strlen (name) + 2];
+ snprintf (tmpdir_name, sizeof tmpdir_name, "%s/%s",
+ g->tmpdir, name);
+
+ if (guestfs___download_to_tmp (g, "/var/lib/rpm/Name", name,
+ MAX_PKG_DB_SIZE) == -1)
+ return NULL;
+
+ const char *pkgs = "rpm_Packages";
+ char tmpdir_pkgs[strlen (g->tmpdir) + strlen (pkgs) + 2];
+ snprintf (tmpdir_pkgs, sizeof tmpdir_pkgs, "%s/%s",
+ g->tmpdir, pkgs);
- if (guestfs___download_to_tmp (g, "/var/lib/rpm/Name", basename,
+ if (guestfs___download_to_tmp (g, "/var/lib/rpm/Packages", pkgs,
MAX_PKG_DB_SIZE) == -1)
return NULL;
+ /* Allocate interim structure to store names and links. */
+ struct rpm_names_list list;
+ list.names = NULL;
+ list.len = 0;
+
+ /* Read Name database. */
+ if (guestfs___read_db_dump (g, tmpdir_name, &list, read_rpm_name) == -1) {
+ free_rpm_names_list (&list);
+ return NULL;
+ }
+
+ /* Sort the names by link field for fast searching. */
+ qsort (list.names, list.len, sizeof (struct rpm_name), compare_links);
+
/* Allocate 'apps' list. */
struct guestfs_application_list *apps;
apps = safe_malloc (g, sizeof *apps);
apps->len = 0;
apps->val = NULL;
- if (guestfs___read_db_dump (g, tmpdir_basename, apps, read_rpm_name) == -1) {
+ /* Read Packages database. */
+ struct read_package_data data;
+ data.list = &list;
+ data.apps = apps;
+ if (guestfs___read_db_dump (g, tmpdir_pkgs, &data, read_package) == -1) {
+ free_rpm_names_list (&list);
guestfs_free_application_list (apps);
return NULL;
}
+ free_rpm_names_list (&list);
+
return apps;
}