Blame SOURCES/0011-inspection-Fix-inspection-of-recent-RPM-guests-using.patch

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