From 2ff8ad25680373997c2975d7ca51f3af7115eb01 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Fri, 26 Mar 2021 12:19:28 +0000
Subject: [PATCH] inspection: Fix inspection of recent RPM guests using
non-BDB.
Recent RPM-based guests have switched from using Berkeley DB (BDB) to
sqlite. In order to inspect these guests (and earlier ones) we need
to stop using the hokey parsing of the BDB and use librpm APIs
instead.
This commit adds a new internal API so we can call librpm from the
daemon, and changes the library part to use the new API for RPM-based
guests.
This change removes the requirement for BDB tools like db_dump.
See also:
http://lists.rpm.org/pipermail/rpm-ecosystem/2021-March/000751.html
http://lists.rpm.org/pipermail/rpm-ecosystem/2021-March/000754.html
https://blog.fpmurphy.com/2011/08/programmatically-retrieve-rpm-package-details.html
This breaks the virt-inspector test (now in the separate guestfs-tools
repository). However this is not a bug in libguestfs, but a bug in
the phoney Fedora guest that we use for testing - we created a
BDB-style RPM database which was supposed to be just enough to make
the old code work. The new code using real librpm needs
/usr/lib/rpm/rpmrc (not present in the phoney image) and also cannot
parse the phoney database, so we will need to separately rework that
test.
Thanks: Panu Matilainen
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1766487
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1409024
(cherry picked from commit c9ee831affed55abe0f928134cbbd2ed83b2f510)
---
.gitignore | 1 +
appliance/packagelist.in | 2 +
daemon/Makefile.am | 8 +-
daemon/rpm-c.c | 161 ++++++++++++++++++++
daemon/rpm.ml | 58 +++++++
docs/C_SOURCE_FILES | 2 +-
docs/guestfs-building.pod | 9 +-
generator/actions_inspection.ml | 10 ++
generator/proc_nr.ml | 1 +
lib/MAX_PROC_NR | 2 +-
lib/Makefile.am | 1 -
lib/dbdump.c | 229 ----------------------------
lib/guestfs-internal.h | 4 -
lib/inspect-apps.c | 259 +-------------------------------
m4/guestfs-daemon.m4 | 8 +
m4/guestfs-progs.m4 | 10 --
16 files changed, 258 insertions(+), 507 deletions(-)
create mode 100644 daemon/rpm-c.c
create mode 100644 daemon/rpm.ml
delete mode 100644 lib/dbdump.c
diff --git a/.gitignore b/.gitignore
index 7a4696b90..c8b53cac9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -176,6 +176,7 @@ Makefile.in
/daemon/optgroups.mli
/daemon/parted.mli
/daemon/realpath.mli
+/daemon/rpm.mli
/daemon/stamp-guestfsd.pod
/daemon/statvfs.mli
/daemon/structs-cleanups.c
diff --git a/appliance/packagelist.in b/appliance/packagelist.in
index 25f2645d2..15af4284a 100644
--- a/appliance/packagelist.in
+++ b/appliance/packagelist.in
@@ -41,6 +41,7 @@ ifelse(REDHAT,1,
openssh-clients
policycoreutils
reiserfs-utils
+ rpm-libs
syslinux-extlinux
systemd dnl for /sbin/reboot and udevd
vim-minimal
@@ -71,6 +72,7 @@ dnl iproute has been renamed to iproute2
isc-dhcp-client
ldmtool
libc-bin
+ librpm9
linux-image
dnl syslinux 'suggests' mtools, but in reality it's a hard dependency:
mtools
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index df9dcc4ee..7f2d13414 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -57,6 +57,7 @@ generator_built = \
optgroups.mli \
parted.mli \
realpath.mli \
+ rpm.mli \
statvfs.mli \
structs.ml \
structs.mli
@@ -167,6 +168,7 @@ guestfsd_SOURCES = \
proto.c \
readdir.c \
rename.c \
+ rpm-c.c \
rsync.c \
scrub.c \
selinux.c \
@@ -231,6 +233,7 @@ guestfsd_LDADD = \
$(LIB_CLOCK_GETTIME) \
$(LIBINTL) \
$(PCRE_LIBS) \
+ $(LIBRPM_LIBS) \
$(TSK_LIBS) \
$(RPC_LIBS) \
$(YARA_LIBS) \
@@ -260,7 +263,8 @@ guestfsd_CFLAGS = \
$(HIVEX_CFLAGS) \
$(SD_JOURNAL_CFLAGS) \
$(JANSSON_CFLAGS) \
- $(PCRE_CFLAGS)
+ $(PCRE_CFLAGS) \
+ $(LIBRPM_CFLAGS)
# Parts of the daemon are written in OCaml. These are linked into a
# library and then linked to the daemon. See
@@ -298,6 +302,7 @@ SOURCES_MLI = \
optgroups.mli \
parted.mli \
realpath.mli \
+ rpm.mli \
statvfs.mli \
structs.mli \
sysroot.mli \
@@ -338,6 +343,7 @@ SOURCES_ML = \
inspect_fs_windows.ml \
inspect_fs.ml \
inspect.ml \
+ rpm.ml \
callbacks.ml \
daemon.ml
diff --git a/daemon/rpm-c.c b/daemon/rpm-c.c
new file mode 100644
index 000000000..92a3abf58
--- /dev/null
+++ b/daemon/rpm-c.c
@@ -0,0 +1,161 @@
+/* libguestfs - the guestfsd daemon
+ * Copyright (C) 2021 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <caml/alloc.h>
+#include <caml/fail.h>
+#include <caml/memory.h>
+#include <caml/mlvalues.h>
+
+#ifdef HAVE_LIBRPM
+#include <rpm/rpmlib.h>
+#include <rpm/header.h>
+#include <rpm/rpmts.h>
+#include <rpm/rpmdb.h>
+#endif
+
+#include "daemon.h"
+#include "actions.h"
+
+/* Very lightweight OCaml bindings for librpm. */
+
+#pragma GCC diagnostic ignored "-Wimplicit-function-declaration"
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+
+#ifndef HAVE_LIBRPM
+
+value __attribute__((noreturn))
+guestfs_int_daemon_rpm_init (value unitv)
+{
+ CAMLparam1 (unitv);
+ caml_failwith ("no support for RPM guests because "
+ "librpm was missing at compile time");
+}
+
+value __attribute__((noreturn))
+guestfs_int_daemon_rpm_start_iterator (value unitv)
+{
+ guestfs_int_daemon_rpm_init (unitv);
+}
+
+value __attribute__((noreturn))
+guestfs_int_daemon_rpm_next_application (value unitv)
+{
+ guestfs_int_daemon_rpm_init (unitv);
+}
+
+value __attribute__((noreturn))
+guestfs_int_daemon_rpm_end_iterator (value unitv)
+{
+ guestfs_int_daemon_rpm_init (unitv);
+}
+
+#else /* HAVE_LIBRPM */
+
+value
+guestfs_int_daemon_rpm_init (value unitv)
+{
+ CAMLparam1 (unitv);
+ rpmReadConfigFiles (NULL, NULL);
+ CAMLreturn (Val_unit);
+}
+
+static rpmts ts;
+static rpmdbMatchIterator iter;
+
+value
+guestfs_int_daemon_rpm_start_iterator (value unitv)
+{
+ CAMLparam1 (unitv);
+ ts = rpmtsCreate ();
+ iter = rpmtsInitIterator (ts, RPMDBI_PACKAGES, NULL, 0);
+ CAMLreturn (Val_unit);
+}
+
+value
+guestfs_int_daemon_rpm_next_application (value unitv)
+{
+ CAMLparam1 (unitv);
+ CAMLlocal2 (rv, sv);
+ Header h;
+ guestfs_int_application2 app = { 0 };
+
+ h = rpmdbNextIterator (iter);
+ if (h == NULL) caml_raise_not_found ();
+
+ h = headerLink (h);
+ app.app2_name = headerFormat (h, "%{NAME}", NULL);
+ // XXXapp.app2_epoch = headerFormat (h, "%{NAME}", NULL);
+ app.app2_version = headerFormat (h, "%{VERSION}", NULL);
+ app.app2_release = headerFormat (h, "%{RELEASE}", NULL);
+ app.app2_arch = headerFormat (h, "%{ARCH}", NULL);
+ app.app2_url = headerFormat (h, "%{URL}", NULL);
+ app.app2_summary = headerFormat (h, "%{SUMMARY}", NULL);
+ app.app2_description = headerFormat (h, "%{DESCRIPTION}", NULL);
+ headerFree (h);
+
+ /* Convert this to an OCaml struct. Any NULL fields must be turned
+ * into empty string.
+ */
+ rv = caml_alloc (17, 0);
+#define TO_CAML_STRING(i, name) \
+ sv = caml_copy_string (app.name ? app.name : ""); \
+ Store_field (rv, i, sv); \
+ free (app.name)
+
+ TO_CAML_STRING (0, app2_name);
+ TO_CAML_STRING (1, app2_display_name);
+ sv = caml_copy_int32 (app.app2_epoch);
+ Store_field (rv, 2, sv);
+ TO_CAML_STRING (3, app2_version);
+ TO_CAML_STRING (4, app2_release);
+ TO_CAML_STRING (5, app2_arch);
+ TO_CAML_STRING (6, app2_install_path);
+ TO_CAML_STRING (7, app2_trans_path);
+ TO_CAML_STRING (8, app2_publisher);
+ TO_CAML_STRING (9, app2_url);
+ TO_CAML_STRING (10, app2_source_package);
+ TO_CAML_STRING (11, app2_summary);
+ TO_CAML_STRING (12, app2_description);
+ TO_CAML_STRING (13, app2_spare1);
+ TO_CAML_STRING (14, app2_spare2);
+ TO_CAML_STRING (15, app2_spare3);
+ TO_CAML_STRING (16, app2_spare4);
+#undef TO_CAML_STRING
+
+ CAMLreturn (rv);
+}
+
+value
+guestfs_int_daemon_rpm_end_iterator (value unitv)
+{
+ CAMLparam1 (unitv);
+ rpmdbFreeIterator (iter);
+ rpmtsFree (ts);
+ CAMLreturn (Val_unit);
+}
+
+#endif /* HAVE_LIBRPM */
diff --git a/daemon/rpm.ml b/daemon/rpm.ml
new file mode 100644
index 000000000..f61ce41c5
--- /dev/null
+++ b/daemon/rpm.ml
@@ -0,0 +1,58 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2021 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open Printf
+
+open Std_utils
+
+external rpm_init : unit -> unit = "guestfs_int_daemon_rpm_init"
+external rpm_start_iterator : unit -> unit = "guestfs_int_daemon_rpm_start_iterator"
+external rpm_next_application : unit -> Structs.application2 = "guestfs_int_daemon_rpm_next_application"
+external rpm_end_iterator : unit -> unit = "guestfs_int_daemon_rpm_end_iterator"
+
+(* librpm is troublesome when run from the main process. In
+ * particular it holds open some glibc NSS files. Therefore we fork
+ * before doing the chroot and any librpm operations.
+ *
+ * We could also consider in future limiting the time taken to run the
+ * subprocess since it's unclear that parsing RPM config files from
+ * the guest in particular is safe.
+ *)
+let rec internal_list_rpm_applications () =
+ let chroot = Chroot.create ~name:"librpm" () in
+ let apps = Chroot.f chroot list_rpm_applications () in
+ eprintf "librpm returned %d installed packages\n%!" (List.length apps);
+ apps
+
+and list_rpm_applications () =
+ rpm_init ();
+ rpm_start_iterator ();
+ let ret = ref [] in
+ let rec loop () =
+ try
+ let app = rpm_next_application () in
+ List.push_front app ret;
+ loop ()
+ with Not_found -> ()
+ in
+ loop ();
+ rpm_end_iterator ();
+ List.sort
+ (fun { Structs.app2_name = n1 } { Structs.app2_name = n2 } ->
+ compare n1 n2)
+ !ret
diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES
index 831b7e25a..8b6aa8896 100644
--- a/docs/C_SOURCE_FILES
+++ b/docs/C_SOURCE_FILES
@@ -149,6 +149,7 @@ daemon/pingdaemon.c
daemon/proto.c
daemon/readdir.c
daemon/rename.c
+daemon/rpm-c.c
daemon/rsync.c
daemon/scrub.c
daemon/selinux-relabel.c
@@ -293,7 +294,6 @@ lib/command.c
lib/conn-socket.c
lib/copy-in-out.c
lib/create.c
-lib/dbdump.c
lib/drives.c
lib/errors.c
lib/event-string.c
diff --git a/docs/guestfs-building.pod b/docs/guestfs-building.pod
index 28c761ce4..4d75772d0 100644
--- a/docs/guestfs-building.pod
+++ b/docs/guestfs-building.pod
@@ -210,11 +210,6 @@ eg. F</etc/libguestfs-tools.conf>.
Optional. Used by the L<libvirt backend|guestfs(3)/BACKEND> to
securely confine the appliance (sVirt).
-=item Berkeley DB utils (db_dump, db_load, etc)
-
-Optional. Usually found in a package called C<db-utils>,
-C<db4-utils>, C<db4.X-utils> etc.
-
=item systemtap
Optional. For userspace probes.
@@ -252,6 +247,10 @@ Optional. Render icons from guests.
Optional. Render icons from Windows guests.
+=item librpm
+
+Optional. To parse the list of applications from RPM-based guests.
+
=item Perl C<Expect>
Optional. Perl module used to test L<virt-rescue(1)>.
diff --git a/generator/actions_inspection.ml b/generator/actions_inspection.ml
index e2098cb00..690afd460 100644
--- a/generator/actions_inspection.ml
+++ b/generator/actions_inspection.ml
@@ -607,6 +607,16 @@ Please read L<guestfs(3)/INSPECTION> for more details.
See also C<guestfs_inspect_get_mountpoints>,
C<guestfs_inspect_get_filesystems>." };
+ { defaults with
+ name = "internal_list_rpm_applications"; added = (1, 45, 3);
+ style = RStructList ("applications2", "application2"), [], [];
+ visibility = VInternal;
+ impl = OCaml "Rpm.internal_list_rpm_applications";
+ shortdesc = "get applications from RPM guest";
+ longdesc = "\
+This internal function is used by C<guestfs_inspect_list_applications2>
+to list the applications for RPM guests."};
+
]
let non_daemon_functions = [
diff --git a/generator/proc_nr.ml b/generator/proc_nr.ml
index 57976be36..6b6cb7353 100644
--- a/generator/proc_nr.ml
+++ b/generator/proc_nr.ml
@@ -514,6 +514,7 @@ let proc_nr = [
507, "luks_uuid";
508, "cryptsetup_open";
509, "cryptsetup_close";
+510, "internal_list_rpm_applications";
]
(* End of list. If adding a new entry, add it at the end of the list
diff --git a/lib/MAX_PROC_NR b/lib/MAX_PROC_NR
index 77afe238f..2bc4cd64b 100644
--- a/lib/MAX_PROC_NR
+++ b/lib/MAX_PROC_NR
@@ -1 +1 @@
-509
+510
diff --git a/lib/Makefile.am b/lib/Makefile.am
index f9cc53df1..7f36ae515 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -81,7 +81,6 @@ libguestfs_la_SOURCES = \
conn-socket.c \
copy-in-out.c \
create.c \
- dbdump.c \
drives.c \
errors.c \
event-string.c \
diff --git a/lib/dbdump.c b/lib/dbdump.c
deleted file mode 100644
index 7c17ce6b3..000000000
--- a/lib/dbdump.c
+++ /dev/null
@@ -1,229 +0,0 @@
-/* libguestfs
- * Copyright (C) 2010-2012 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
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/wait.h>
-#include <libintl.h>
-
-#ifdef HAVE_ENDIAN_H
-#include <endian.h>
-#endif
-
-#include "guestfs.h"
-#include "guestfs-internal.h"
-
-#if defined(DB_DUMP)
-
-static void read_db_dump_line (guestfs_h *g, void *datav, const char *line, size_t len);
-static unsigned char *convert_hex_to_binary (guestfs_h *g, const char *hex, size_t hexlen, size_t *binlen_rtn);
-
-struct cb_data {
- guestfs_int_db_dump_callback callback;
- void *opaque;
- enum { reading_header,
- reading_key, reading_value,
- reading_finished,
- reading_failed } state;
- unsigned char *key;
- size_t keylen;
-};
-
-/* This helper function is specialized to just reading the hash-format
- * output from db_dump/db4_dump. It's just enough to support the RPM
- * database format.
- */
-int
-guestfs_int_read_db_dump (guestfs_h *g,
- const char *dumpfile, void *opaque,
- guestfs_int_db_dump_callback callback)
-{
- struct cb_data data;
- CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g);
- int r;
-
- data.callback = callback;
- data.opaque = opaque;
- data.state = reading_header;
- data.key = NULL;
-
- guestfs_int_cmd_add_arg (cmd, DB_DUMP);
- guestfs_int_cmd_add_arg (cmd, "-k");
- guestfs_int_cmd_add_arg (cmd, dumpfile);
- guestfs_int_cmd_set_stdout_callback (cmd, read_db_dump_line, &data, 0);
-
- r = guestfs_int_cmd_run (cmd);
- free (data.key);
-
- if (r == -1)
- return -1;
- if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
- guestfs_int_external_command_failed (g, r, DB_DUMP, NULL);
- return -1;
- }
- if (data.state != reading_finished) {
- error (g, _("%s: unexpected error or end of output"), DB_DUMP);
- return -1;
- }
-
- return 0;
-}
-
-static void
-read_db_dump_line (guestfs_h *g, void *datav, const char *line, size_t len)
-{
- struct cb_data *data = datav;
-
- switch (data->state) {
- case reading_finished:
- case reading_failed:
- return;
-
- case reading_header:
- /* Ignore everything to end-of-header marker. */
- if (STRPREFIX (line, "HEADER=END"))
- data->state = reading_key;
- return;
-
- /* Read the key, value pairs using a state machine. They are
- * prefixed with a space and printed as hex strings, so convert
- * those strings to binary. Pass the strings up to the callback
- * function.
- */
- case reading_key:
- if (STRPREFIX (line, "DATA=END")) {
- data->state = reading_finished;
- return;
- }
-
- if (len < 1 || line[0] != ' ') {
- debug (g, _("unexpected line from db_dump command, no space prefix"));
- data->state = reading_failed;
- return;
- }
-
- data->key = convert_hex_to_binary (g, &line[1], len-1, &data->keylen);
- if (data->key == NULL) {
- data->state = reading_failed;
- return;
- }
-
- data->state = reading_value;
- return;
-
- case reading_value: {
- CLEANUP_FREE unsigned char *value = NULL;
- size_t valuelen;
-
- if (len < 1 || line[0] != ' ') {
- debug (g, _("unexpected line from db_dump command, no space prefix"));
- data->state = reading_failed;
- return;
- }
-
- value = convert_hex_to_binary (g, &line[1], len-1, &valuelen);
- if (value == NULL) {
- data->state = reading_failed;
- return;
- }
-
- if (data->callback (g, data->key, data->keylen,
- value, valuelen, data->opaque) == -1) {
- data->state = reading_failed;
- return;
- }
-
- free (data->key);
- data->key = NULL;
-
- data->state = reading_key;
- return;
- }
- }
-}
-
-static int
-convert_hex_octet (const char *h)
-{
- int r;
-
- switch (h[0]) {
- case 'a'...'f':
- r = (h[0] - 'a' + 10) << 4;
- break;
- case 'A'...'F':
- r = (h[0] - 'A' + 10) << 4;
- break;
- case '0'...'9':
- r = (h[0] - '0') << 4;
- break;
- default:
- return -1;
- }
-
- switch (h[1]) {
- case 'a'...'f':
- r |= h[1] - 'a' + 10;
- break;
- case 'A'...'F':
- r |= h[1] - 'A' + 10;
- break;
- case '0'...'9':
- r |= h[1] - '0';
- break;
- default:
- return -1;
- }
-
- return r;
-}
-
-static unsigned char *
-convert_hex_to_binary (guestfs_h *g, const char *hex, size_t hexlen,
- size_t *binlen_rtn)
-{
- unsigned char *bin;
- size_t binlen;
- size_t i, o;
- int b;
-
- if (hexlen > 0 && hex[hexlen-1] == '\n')
- hexlen--;
-
- binlen = hexlen / 2;
- bin = safe_malloc (g, binlen);
-
- for (i = o = 0; i+1 < hexlen && o < binlen; i += 2, ++o) {
- b = convert_hex_octet (&hex[i]);
- if (b >= 0)
- bin[o] = b;
- else {
- error (g, _("unexpected non-hex digits in output of db_dump command"));
- free (bin);
- return NULL;
- }
- }
-
- *binlen_rtn = binlen;
- return bin;
-}
-
-#endif /* defined(DB_DUMP) */
diff --git a/lib/guestfs-internal.h b/lib/guestfs-internal.h
index d7ec7215d..4799ee0a1 100644
--- a/lib/guestfs-internal.h
+++ b/lib/guestfs-internal.h
@@ -719,10 +719,6 @@ extern int guestfs_int_set_backend (guestfs_h *g, const char *method);
/* inspect.c */
extern char *guestfs_int_download_to_tmp (guestfs_h *g, const char *filename, const char *extension, uint64_t max_size);
-/* dbdump.c */
-typedef int (*guestfs_int_db_dump_callback) (guestfs_h *g, const unsigned char *key, size_t keylen, const unsigned char *value, size_t valuelen, void *opaque);
-extern int guestfs_int_read_db_dump (guestfs_h *g, const char *dumpfile, void *opaque, guestfs_int_db_dump_callback callback);
-
/* lpj.c */
extern int guestfs_int_get_lpj (guestfs_h *g);
diff --git a/lib/inspect-apps.c b/lib/inspect-apps.c
index dbc9d968c..da0003672 100644
--- a/lib/inspect-apps.c
+++ b/lib/inspect-apps.c
@@ -47,22 +47,12 @@
/* Some limits on what the inspection code will read, for safety. */
-/* Maximum RPM 'Packages' file we will download to /tmp. This file
- * 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 400 MB databases.
- */
-#define MAX_RPM_PACKAGES_SIZE (500 * 1000 * 1000)
-/* Maximum RPM 'Name' file we will download to /tmp. */
-#define MAX_RPM_NAME_SIZE (50 * 1000 * 1000)
/* Maximum dpkg 'status' file we will download to /tmp. */
#define MAX_DPKG_STATUS_SIZE (50 * 1000 * 1000)
/* Maximum APK 'installed' file we will download to /tmp. */
#define MAX_APK_INSTALLED_SIZE (50 * 1000 * 1000)
-#ifdef DB_DUMP
static struct guestfs_application2_list *list_applications_rpm (guestfs_h *g, const char *root);
-#endif
static struct guestfs_application2_list *list_applications_deb (guestfs_h *g, const char *root);
static struct guestfs_application2_list *list_applications_pacman (guestfs_h *g, const char *root);
static struct guestfs_application2_list *list_applications_apk (guestfs_h *g, const char *root);
@@ -136,11 +126,9 @@ guestfs_impl_inspect_list_applications2 (guestfs_h *g, const char *root)
if (STREQ (type, "linux") || STREQ (type, "hurd")) {
if (STREQ (package_format, "rpm")) {
-#ifdef DB_DUMP
ret = list_applications_rpm (g, root);
if (ret == NULL)
return NULL;
-#endif
}
else if (STREQ (package_format, "deb")) {
ret = list_applications_deb (g, root);
@@ -178,254 +166,15 @@ guestfs_impl_inspect_list_applications2 (guestfs_h *g, const char *root)
return ret;
}
-#ifdef DB_DUMP
-
-/* This data comes from the Name database, and contains the application
- * names and the first 4 bytes of each 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 *listv)
-{
- struct rpm_names_list *list = listv;
- const unsigned char *link_p;
- char *name;
-
- /* Ignore bogus entries. */
- if (keylen == 0 || valuelen < 4)
- return 0;
-
- /* A name entry will have as many links as installed instances of
- * that package. For example, if glibc.i686 and glibc.x86_64 are
- * both installed, then there will be a link for each Packages
- * entry. Add an entry onto list for all installed instances.
- */
- for (link_p = value; link_p < value + valuelen; link_p += 8) {
- name = safe_strndup (g, (const char *) key, keylen);
-
- 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, link_p, 4);
- list->len++;
- }
-
- return 0;
-}
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wcast-align"
-
-/* tag constants, see rpmtag.h in RPM for complete list */
-#define RPMTAG_VERSION 1001
-#define RPMTAG_RELEASE 1002
-#define RPMTAG_EPOCH 1003
-#define RPMTAG_ARCH 1022
-#define RPMTAG_URL 1020
-#define RPMTAG_SUMMARY 1004
-#define RPMTAG_DESCRIPTION 1005
-
-static char *
-get_rpm_header_tag (guestfs_h *g, const unsigned char *header_start,
- size_t header_len, uint32_t tag, char type)
-{
- uint32_t num_fields, offset;
- const unsigned char *cursor = header_start + 8, *store, *header_end;
- size_t max_len;
- char iv[4];
-
- /* This function parses the RPM header structure to pull out various
- * tag strings (version, release, arch, etc.). For more detail on the
- * header format, see:
- * http://www.rpm.org/max-rpm/s1-rpm-file-format-rpm-file-format.html#S2-RPM-FILE-FORMAT-HEADER
- */
-
- /* The minimum header size that makes sense here is 24 bytes. Four
- * bytes for number of fields, followed by four bytes denoting the
- * size of the store, then 16 bytes for the first index entry.
- */
- if (header_len < 24)
- return NULL;
-
- num_fields = be32toh (*(uint32_t *) header_start);
- store = header_start + 8 + (16 * num_fields);
-
- /* The first byte *after* the buffer. If you are here, you've gone
- * too far! */
- header_end = header_start + header_len;
-
- while (cursor < store && cursor <= header_end - 16) {
- if (be32toh (*(uint32_t *) cursor) == tag) {
- offset = be32toh(*(uint32_t *) (cursor + 8));
-
- if (store + offset >= header_end)
- return NULL;
- max_len = header_end - (store + offset);
-
- switch (type) {
- case 's':
- return safe_strndup (g, (const char *) (store + offset), max_len);
-
- case 'i':
- memset (iv, 0, sizeof iv);
- memcpy (iv, (void *) (store + offset),
- max_len > sizeof iv ? sizeof iv : max_len);
- return safe_memdup (g, iv, sizeof iv);
-
- default:
- abort ();
- }
- }
- cursor += 16;
- }
-
- return NULL;
-}
-
-struct read_package_data {
- struct rpm_names_list *list;
- struct guestfs_application2_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;
- CLEANUP_FREE char *version = NULL, *release = NULL,
- *epoch_str = NULL, *arch = NULL, *url = NULL, *summary = NULL,
- *description = NULL;
- int32_t epoch;
-
- /* 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 header data
- * 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.
- */
-
- version = get_rpm_header_tag (g, value, valuelen, RPMTAG_VERSION, 's');
- release = get_rpm_header_tag (g, value, valuelen, RPMTAG_RELEASE, 's');
- epoch_str = get_rpm_header_tag (g, value, valuelen, RPMTAG_EPOCH, 'i');
- arch = get_rpm_header_tag (g, value, valuelen, RPMTAG_ARCH, 's');
- url = get_rpm_header_tag (g, value, valuelen, RPMTAG_URL, 's');
- summary = get_rpm_header_tag (g, value, valuelen, RPMTAG_SUMMARY, 's');
- description = get_rpm_header_tag (g, value, valuelen, RPMTAG_DESCRIPTION, 's');
-
- /* The epoch is stored as big-endian integer. */
- if (epoch_str)
- epoch = be32toh (*(int32_t *) epoch_str);
- else
- epoch = 0;
-
- /* Add the application and what we know. */
- if (version && release)
- add_application (g, data->apps, entry->name, "", epoch, version, release,
- arch ? arch : "", "", "", url ? : "", "",
- summary ? : "", description ? : "");
-
- return 0;
-}
-
-#pragma GCC diagnostic pop
-
static struct guestfs_application2_list *
list_applications_rpm (guestfs_h *g, const char *root)
{
- CLEANUP_FREE char *Name = NULL, *Packages = NULL;
- struct rpm_names_list list = { .names = NULL, .len = 0 };
- struct guestfs_application2_list *apps = NULL;
- struct read_package_data data;
-
- Name = guestfs_int_download_to_tmp (g, "/var/lib/rpm/Name", NULL,
- MAX_RPM_NAME_SIZE);
- if (Name == NULL)
- goto error;
-
- Packages = guestfs_int_download_to_tmp (g, "/var/lib/rpm/Packages", NULL,
- MAX_RPM_PACKAGES_SIZE);
- if (Packages == NULL)
- goto error;
-
- /* Read Name database. */
- if (guestfs_int_read_db_dump (g, Name, &list, read_rpm_name) == -1)
- goto error;
-
- /* Sort the names by link field for fast searching. */
- qsort (list.names, list.len, sizeof (struct rpm_name), compare_links);
-
- /* Allocate 'apps' list. */
- apps = safe_malloc (g, sizeof *apps);
- apps->len = 0;
- apps->val = NULL;
-
- /* Read Packages database. */
- data.list = &list;
- data.apps = apps;
- if (guestfs_int_read_db_dump (g, Packages, &data, read_package) == -1)
- goto error;
-
- free_rpm_names_list (&list);
-
- return apps;
-
- error:
- free_rpm_names_list (&list);
- guestfs_free_application2_list (apps);
-
- return NULL;
+ /* We don't need the ‘root’ parameter here. The caller is supposed
+ * to have mounted the guest up before calling the public API.
+ */
+ return guestfs_internal_list_rpm_applications (g);
}
-#endif /* defined DB_DUMP */
-
static struct guestfs_application2_list *
list_applications_deb (guestfs_h *g, const char *root)
{
diff --git a/m4/guestfs-daemon.m4 b/m4/guestfs-daemon.m4
index aa90268b4..1728249a5 100644
--- a/m4/guestfs-daemon.m4
+++ b/m4/guestfs-daemon.m4
@@ -102,6 +102,14 @@ PKG_CHECK_MODULES([HIVEX], [hivex],[
[AC_MSG_FAILURE([hivex library is required])])
AM_CONDITIONAL([HAVE_HIVEX],[test "x$HIVEX_LIBS" != "x"])
+dnl librpm library (optional)
+PKG_CHECK_MODULES([LIBRPM], [rpm >= 4.6.0],[
+ AC_SUBST([LIBRPM_CFLAGS])
+ AC_SUBST([LIBRPM_LIBS])
+ AC_DEFINE([HAVE_LIBRPM],[1],[librpm library found at compile time.])
+],[AC_MSG_WARN([librpm library not found])]
+)
+
dnl systemd journal library (optional)
PKG_CHECK_MODULES([SD_JOURNAL], [libsystemd],[
AC_SUBST([SD_JOURNAL_CFLAGS])
diff --git a/m4/guestfs-progs.m4 b/m4/guestfs-progs.m4
index bf1f83c9d..f90bda04e 100644
--- a/m4/guestfs-progs.m4
+++ b/m4/guestfs-progs.m4
@@ -63,16 +63,6 @@ AC_CHECK_PROG([PO4A_GETTEXTIZE],[po4a-gettextize],[po4a-gettextize],[no])
AC_CHECK_PROG([PO4A_TRANSLATE],[po4a-translate],[po4a-translate],[no])
AM_CONDITIONAL([HAVE_PO4A], [test "x$PO4A_GETTEXTIZE" != "xno" && test "x$PO4A_TRANSLATE" != "xno"])
-dnl Check for db_dump, db_load (optional).
-GUESTFS_FIND_DB_TOOL([DB_DUMP], [dump])
-GUESTFS_FIND_DB_TOOL([DB_LOAD], [load])
-if test "x$DB_DUMP" != "xno"; then
- AC_DEFINE_UNQUOTED([DB_DUMP],["$DB_DUMP"],[Name of db_dump program.])
-fi
-if test "x$DB_LOAD" != "xno"; then
- AC_DEFINE_UNQUOTED([DB_LOAD],["$DB_LOAD"],[Name of db_load program.])
-fi
-
dnl Check for netpbm programs (optional).
AC_PATH_PROGS([PBMTEXT],[pbmtext],[no])
AC_PATH_PROGS([PNMTOPNG],[pnmtopng],[no])
--
2.31.1