]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
debuginfod: add metadata query by buildid
authorJosef Cejka <jcejka@suse.de>
Wed, 18 Mar 2026 11:02:13 +0000 (12:02 +0100)
committerAaron Merey <amerey@redhat.com>
Sun, 29 Mar 2026 23:57:37 +0000 (19:57 -0400)
This patch extends current metadata by search by buildid.
It allows clients to query for all packages (with exact versions)
and files associated with a specific buildid.

I'd like to use the exact package versions during core dump
analysis to match them with already known issues/bugs,
to check that involved packages are reasonable up-to-date
and to identify other invalid setup, such as mixing packages
from different Linux distros.

Search by buildid prepares alternative db query, handling
of results incl. gathering results from federated servers
remains the same.
It does not search for source files or source file archives.

Signed-off-by: Josef Cejka <jcejka@suse.de>
debuginfod/debuginfod-find.c
debuginfod/debuginfod.cxx
doc/debuginfod-find.1
tests/run-debuginfod-find-metadata.sh

index 75fb1101ebc19c39c8ecd1db4571d514093cee3c..d96c92af24f2f05a1482c131e17e524bc8c8a397 100644 (file)
@@ -54,7 +54,7 @@ static const char args_doc[] = N_("debuginfo BUILDID\n"
                                   "source PATH /FILENAME\n"
                                   "section BUILDID SECTION-NAME\n"
                                   "section PATH SECTION-NAME\n"
-                                  "metadata (glob|file|KEY) (GLOB|FILENAME|VALUE)\n"
+                                  "metadata (glob|file|buildid|KEY) (GLOB|FILENAME|BUILDID|VALUE)\n"
                                   );
 
 /* Definitions of arguments for argp functions.  */
index 049570ae6cf46b95850f4c26bfe7c876c3b4b651..2cf77bcc9127771440c2c9146503ef56929dd5b1 100644 (file)
@@ -3578,25 +3578,9 @@ handle_metrics (off_t* size)
   return r;
 }
 
-
-static struct MHD_Response*
-handle_metadata (MHD_Connection* conn,
-                 string key, string value, off_t* size)
+static sqlite_ps*
+handle_metadata_glob(sqlite3* thisdb, const string& key, const string& value)
 {
-  MHD_Response* r;
-  // Because this query can take on the order of many seconds, we need
-  // to prevent DoS against the other normal quick queries, so we use
-  // a dedicated database connection.
-  sqlite3 *thisdb = 0;
-  int rc = sqlite3_open_v2 (db_path.c_str(), &thisdb, (SQLITE_OPEN_READONLY
-                                                       |SQLITE_OPEN_URI
-                                                       |SQLITE_OPEN_PRIVATECACHE
-                                                       |SQLITE_OPEN_NOMUTEX), /* private to us */
-                            NULL);
-  if (rc)
-    throw sqlite_exception(rc, "cannot open database for metadata query");
-  defer_dtor<sqlite3*,int> sqlite_db_closer (thisdb, sqlite3_close_v2);
-                                           
   // Query locally for matching e, d files
   string op;
   if (key == "glob")
@@ -3670,6 +3654,63 @@ handle_metadata (MHD_Connection* conn,
   pp->bind(2, bname);
   pp->bind(3, dirname);
   pp->bind(4, bname);
+  return pp;
+}
+
+static sqlite_ps*
+handle_metadata_buildid(sqlite3* thisdb, const string& value)
+{
+  string sql = string(
+                      "select d1.executable_p, d1.debuginfo_p, 0 as source_p, "
+                      "       b1.hex, f1d.name || '/' || f1b.name as file, a1.name as archive "
+                      "from " BUILDIDS "_r_de d1, " BUILDIDS "_files f1, " BUILDIDS "_fileparts f1b, " BUILDIDS "_fileparts f1d, "
+                      BUILDIDS "_buildids b1, " BUILDIDS "_files_v a1 "
+                      "where f1.id = d1.content and a1.id = d1.file and d1.buildid = b1.id "
+                      "      and b1.hex = ? and f1.dirname = f1d.id and f1.basename = f1b.id "
+                      "union all \n"
+                      "select d2.executable_p, d2.debuginfo_p, 0, "
+                      "       b2.hex, f2d.name || '/' || f2b.name, NULL "
+                      "from " BUILDIDS "_f_de d2, " BUILDIDS "_files f2, " BUILDIDS "_fileparts f2b, " BUILDIDS "_fileparts f2d, "
+                      BUILDIDS "_buildids b2 "
+                      "where f2.id = d2.file and d2.buildid = b2.id "
+                      "      and b2.hex = ? "
+                      "      and f2.dirname = f2d.id and f2.basename = f2b.id");
+
+  sqlite_ps *pp = new sqlite_ps (thisdb, "mhd-query-meta-buildid", sql);
+  pp->reset();
+  pp->bind(1, value); // Bind buildid for the first select (_r_de)
+  pp->bind(2, value); // Bind buildid for the second select (_f_de)
+  return pp;
+}
+
+static struct MHD_Response*
+handle_metadata (MHD_Connection* conn,
+                 string key, string value, off_t* size)
+{
+  MHD_Response* r;
+  // Because this query can take on the order of many seconds, we need
+  // to prevent DoS against the other normal quick queries, so we use
+  // a dedicated database connection.
+  sqlite3 *thisdb = 0;
+  int rc = sqlite3_open_v2 (db_path.c_str(), &thisdb, (SQLITE_OPEN_READONLY
+                                                       |SQLITE_OPEN_URI
+                                                       |SQLITE_OPEN_PRIVATECACHE
+                                                       |SQLITE_OPEN_NOMUTEX), /* private to us */
+                            NULL);
+  if (rc)
+    throw sqlite_exception(rc, "cannot open database for metadata query");
+  defer_dtor<sqlite3*,int> sqlite_db_closer (thisdb, sqlite3_close_v2);
+
+  sqlite_ps *pp = nullptr;
+
+  if (key == "glob" || key == "file") {
+    pp = handle_metadata_glob(thisdb, key, value);
+  } else if (key == "buildid") {
+    pp = handle_metadata_buildid(thisdb, value);
+  } else {
+    throw reportable_exception("/metadata webapi error, unsupported key");
+  }
+
   unique_ptr<sqlite_ps> ps_closer(pp); // release pp if exception or return
   pp->reset_timeout(metadata_maxtime_s);
       
index 339d5a340d89353e1d974baef71fa588626d82b1..ce5da77329a9c95100749bc8d14b74a00713ec6e 100644 (file)
@@ -141,6 +141,7 @@ KEY VALUE   DESCRIPTION
 
 \fBfile\fP     \fIpath\fP      exact match \fIpath\fP, including in archives
 \fBglob\fP     \fIpattern\fP   shell-style glob match \fIpattern\fP, including in archives, as in fnmatch(FNM_PATHNAME)
+\fBbuildid\fP  \fIhex\fP       all executables and debuginfo files matching the given buildid
 .TE
 
 The resulting output will look something like the following
index 99759cff20a8445fa9e6d28d45002b3c3b0f9454..fd42fb8edfe7ee903a661207abd2543ca0ac4342 100755 (executable)
@@ -75,6 +75,10 @@ RESULTJ=`env LD_LIBRARY_PATH=$ldpath ${VALGRIND_CMD} ${abs_builddir}/../debuginf
 echo $RESULTJ
 N_FOUND=`echo $RESULTJ | jq '.results | length'`
 test $N_FOUND -eq 2
+RESULTJ=`env LD_LIBRARY_PATH=$ldpath ${VALGRIND_CMD} ${abs_builddir}/../debuginfod/debuginfod-find metadata buildid "f17a29b5a25bd4960531d82aa6b07c8abe84fa66"`
+echo $RESULTJ
+N_FOUND=`echo $RESULTJ | jq '.results | length'`
+test $N_FOUND -eq 2
 
 
 # Query via the webapi as well
@@ -85,6 +89,8 @@ curl -s -i http://127.0.0.1:$PORT2'/metadata?key=glob&value=/usr/bin/*hi*' | gre
 test `curl -s http://127.0.0.1:$PORT2'/metadata?key=glob&value=/usr/bin/*hi*' | jq '.results[0].buildid == "f17a29b5a25bd4960531d82aa6b07c8abe84fa66"'` = 'true'
 test `curl -s http://127.0.0.1:$PORT2'/metadata?key=glob&value=/usr/bin/*hi*' | jq '.results[0].file == "/usr/bin/hithere"'` = 'true'
 test `curl -s http://127.0.0.1:$PORT2'/metadata?key=glob&value=/usr/bin/*hi*' | jq '.results[0].archive | test(".*hithere.*deb")'` = 'true'
+test `curl -s http://127.0.0.1:$PORT1'/metadata?key=buildid&value=bc1febfd03ca05e030f0d205f7659db29f8a4b30' | jq '.results[0].archive | test(".*hello2.*rpm")'` = 'true'
+test `curl -s http://127.0.0.1:$PORT2'/metadata?key=buildid&value=bc1febfd03ca05e030f0d205f7659db29f8a4b30' | jq '.results[0].archive | test(".*hello2.*rpm")'` = 'true'
 # Note we query the upstream server too, since the downstream will have an incomplete result due to the badurl
 test `curl -s http://127.0.0.1:$PORT1'/metadata?key=glob&value=/usr/bin/*hi*' | jq '.complete == true'` = 'true'
 test `curl -s http://127.0.0.1:$PORT2'/metadata?key=glob&value=/usr/bin/*hi*' | jq '.complete == false'` = 'true'