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

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