#include <fts.h>
#endif
+struct debuginfod_client
+{
+ /* Progress/interrupt callback function. */
+ debuginfod_progressfn_t progressfn;
+
+ /* Can contain all other context, like cache_path, server_urls,
+ timeout or other info gotten from environment variables, the
+ handle data, etc. So those don't have to be reparsed and
+ recreated on each request. */
+};
/* The cache_clean_interval_s file within the debuginfod cache specifies
how frequently the cache should be cleaned. The file's st_mtime represents
static const char *server_timeout_envvar = DEBUGINFOD_TIMEOUT_ENV_VAR;
static int server_timeout = 5;
-/* Progress/interrupt callback function. */
-static debuginfod_progressfn_t progressfn;
-
/* Data associated with a particular CURL easy handle. Passed to
the write callback. */
struct handle_data
/* Delete any files that have been unmodied for a period
longer than $DEBUGINFOD_CACHE_CLEAN_INTERVAL_S. */
static int
-debuginfod_clean_cache(char *cache_path, char *interval_path, char *max_unused_path)
+debuginfod_clean_cache(debuginfod_client *c,
+ char *cache_path, char *interval_path,
+ char *max_unused_path)
{
struct stat st;
FILE *interval_file;
while ((f = fts_read(fts)) != NULL)
{
files++;
- if (progressfn) /* inform/check progress callback */
- if ((*progressfn) (files, 0))
+ if (c->progressfn) /* inform/check progress callback */
+ if ((c->progressfn) (c, files, 0))
break;
switch (f->fts_info)
descriptor for the target, otherwise return an error code.
*/
static int
-debuginfod_query_server (const unsigned char *build_id,
+debuginfod_query_server (debuginfod_client *c,
+ const unsigned char *build_id,
int build_id_len,
const char *type,
const char *filename,
int rc = debuginfod_init_cache(cache_path, interval_path, maxage_path);
if (rc != 0)
goto out;
- rc = debuginfod_clean_cache(cache_path, interval_path, maxage_path);
+ rc = debuginfod_clean_cache(c, cache_path, interval_path, maxage_path);
if (rc != 0)
goto out;
{
CURLMcode curl_res;
- if (progressfn) /* inform/check progress callback */
+ if (c->progressfn) /* inform/check progress callback */
{
loops ++;
long pa = loops; /* default params for progress callback */
#endif
}
- if ((*progressfn) (pa, pb))
+ if ((*c->progressfn) (c, pa, pb))
break;
}
return rc;
}
-
/* See debuginfod.h */
+debuginfod_client *
+debuginfod_begin (void)
+{
+ debuginfod_client *client;
+ size_t size = sizeof (struct debuginfod_client);
+ client = (debuginfod_client *) malloc (size);
+ if (client != NULL)
+ client->progressfn = NULL;
+ return client;
+}
+
+void
+debuginfod_end (debuginfod_client *client)
+{
+ free (client);
+}
+
int
-debuginfod_find_debuginfo (const unsigned char *build_id, int build_id_len,
+debuginfod_find_debuginfo (debuginfod_client *client,
+ const unsigned char *build_id, int build_id_len,
char **path)
{
- return debuginfod_query_server(build_id, build_id_len,
+ return debuginfod_query_server(client, build_id, build_id_len,
"debuginfo", NULL, path);
}
/* See debuginfod.h */
int
-debuginfod_find_executable(const unsigned char *build_id, int build_id_len,
+debuginfod_find_executable(debuginfod_client *client,
+ const unsigned char *build_id, int build_id_len,
char **path)
{
- return debuginfod_query_server(build_id, build_id_len,
+ return debuginfod_query_server(client, build_id, build_id_len,
"executable", NULL, path);
}
/* See debuginfod.h */
-int debuginfod_find_source(const unsigned char *build_id, int build_id_len,
+int debuginfod_find_source(debuginfod_client *client,
+ const unsigned char *build_id, int build_id_len,
const char *filename, char **path)
{
- return debuginfod_query_server(build_id, build_id_len,
+ return debuginfod_query_server(client, build_id, build_id_len,
"source", filename, path);
}
-debuginfod_progressfn_t
-debuginfod_set_progressfn(debuginfod_progressfn_t fn)
+void
+debuginfod_set_progressfn(debuginfod_client *client,
+ debuginfod_progressfn_t fn)
{
- debuginfod_progressfn_t it = progressfn;
- progressfn = fn;
- return it;
+ client->progressfn = fn;
}
{ NULL, 0, NULL, 0, NULL, 0 }
};
+/* debuginfod connection handle. */
+static debuginfod_client *client;
-
-int progressfn(long a, long b)
+int progressfn(debuginfod_client *c __attribute__((__unused__)),
+ long a, long b)
{
fprintf (stderr, "Progress %ld / %ld\n", a, b);
return 0;
(void) state;
switch (key)
{
- case 'v': debuginfod_set_progressfn (& progressfn); break;
+ case 'v': debuginfod_set_progressfn (client, & progressfn); break;
default: return ARGP_ERR_UNKNOWN;
}
return 0;
int
main(int argc, char** argv)
{
+ client = debuginfod_begin ();
+ if (client == NULL)
+ {
+ fprintf(stderr, "Couldn't create debuginfod client context\n");
+ return 1;
+ }
+
int remaining;
(void) argp_parse (&argp, argc, argv, ARGP_IN_ORDER|ARGP_NO_ARGS, &remaining, NULL);
debuginfod_find_* function. If FILETYPE is "source"
then ensure a FILENAME was also supplied as an argument. */
if (strcmp(argv[remaining], "debuginfo") == 0)
- rc = debuginfod_find_debuginfo((unsigned char *)argv[remaining+1], 0, &cache_name);
+ rc = debuginfod_find_debuginfo(client,
+ (unsigned char *)argv[remaining+1], 0,
+ &cache_name);
else if (strcmp(argv[remaining], "executable") == 0)
- rc = debuginfod_find_executable((unsigned char *)argv[remaining+1], 0, &cache_name);
+ rc = debuginfod_find_executable(client,
+ (unsigned char *)argv[remaining+1], 0,
+ &cache_name);
else if (strcmp(argv[remaining], "source") == 0)
{
if (remaining+2 == argc || argv[3][0] != '/')
fprintf(stderr, "If FILETYPE is \"source\" then absolute /FILENAME must be given\n");
return 1;
}
- rc = debuginfod_find_source((unsigned char *)argv[remaining+1], 0,
- argv[remaining+2], &cache_name);
+ rc = debuginfod_find_source(client, (unsigned char *)argv[remaining+1],
+ 0, argv[remaining+2], &cache_name);
}
else
{
printf("%s\n", cache_name);
free (cache_name);
+ debuginfod_end (client);
+
return 0;
}
static int
-debuginfod_find_progress (long a, long b)
+debuginfod_find_progress (debuginfod_client *, long a, long b)
{
if (verbose > 4)
obatched(clog) << "federated debuginfod progress=" << a << "/" << b << endl;
// is to defer to other debuginfo servers.
int fd = -1;
- if (artifacttype == "debuginfo")
- fd = debuginfod_find_debuginfo ((const unsigned char*) buildid.c_str(), 0,
- NULL);
- else if (artifacttype == "executable")
- fd = debuginfod_find_executable ((const unsigned char*) buildid.c_str(), 0,
- NULL);
- else if (artifacttype == "source")
- fd = debuginfod_find_source ((const unsigned char*) buildid.c_str(), 0,
- suffix.c_str(), NULL);
+ debuginfod_client *client = debuginfod_begin ();
+ if (client != NULL)
+ {
+ debuginfod_set_progressfn (client, & debuginfod_find_progress);
+
+ if (artifacttype == "debuginfo")
+ fd = debuginfod_find_debuginfo (client,
+ (const unsigned char*) buildid.c_str(),
+ 0, NULL);
+ else if (artifacttype == "executable")
+ fd = debuginfod_find_executable (client,
+ (const unsigned char*) buildid.c_str(),
+ 0, NULL);
+ else if (artifacttype == "source")
+ fd = debuginfod_find_source (client,
+ (const unsigned char*) buildid.c_str(),
+ 0, suffix.c_str(), NULL);
+ }
+ else
+ fd = -errno; /* Set by debuginfod_begin. */
+ debuginfod_end (client);
+
if (fd >= 0)
{
inc_metric ("http_responses_total","result","upstream");
"cannot run database schema ddl: %s", sqlite3_errmsg(db));
}
- (void) debuginfod_set_progressfn (& debuginfod_find_progress);
-
// Start httpd server threads. Separate pool for IPv4 and IPv6, in
// case the host only has one protocol stack.
MHD_Daemon *d4 = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
#define DEBUGINFOD_CACHE_PATH_ENV_VAR "DEBUGINFOD_CACHE_PATH"
#define DEBUGINFOD_TIMEOUT_ENV_VAR "DEBUGINFOD_TIMEOUT"
+/* Handle for debuginfod-client connection. */
+typedef struct debuginfod_client debuginfod_client;
+
#ifdef __cplusplus
extern "C" {
#endif
+/* Create a handle for a new debuginfod-client session. */
+debuginfod_client *debuginfod_begin (void);
+
/* Query the urls contained in $DEBUGINFOD_URLS for a file with
the specified type and build id. If build_id_len == 0, the
build_id is supplied as a lowercase hexadecimal string; otherwise
strdup'd copy of the name of the same file in the cache.
Caller must free() it later. */
-int debuginfod_find_debuginfo (const unsigned char *build_id,
+int debuginfod_find_debuginfo (debuginfod_client *client,
+ const unsigned char *build_id,
int build_id_len,
char **path);
-int debuginfod_find_executable (const unsigned char *build_id,
+int debuginfod_find_executable (debuginfod_client *client,
+ const unsigned char *build_id,
int build_id_len,
char **path);
-int debuginfod_find_source (const unsigned char *build_id,
+int debuginfod_find_source (debuginfod_client *client,
+ const unsigned char *build_id,
int build_id_len,
const char *filename,
char **path);
-typedef int (*debuginfod_progressfn_t)(long a, long b);
-debuginfod_progressfn_t debuginfod_set_progressfn(debuginfod_progressfn_t fn);
+typedef int (*debuginfod_progressfn_t)(debuginfod_client *c, long a, long b);
+void debuginfod_set_progressfn(debuginfod_client *c,
+ debuginfod_progressfn_t fn);
+
+/* Release debuginfod client connection context handle. */
+void debuginfod_end (debuginfod_client *client);
#ifdef __cplusplus
}
ELFUTILS_0 { };
ELFUTILS_0.178 {
global:
+ debuginfod_begin;
+ debuginfod_end;
debuginfod_find_debuginfo;
debuginfod_find_executable;
debuginfod_find_source;
--- /dev/null
+.so man3/debuginfod_find_debuginfo.3
--- /dev/null
+.so man3/debuginfod_find_debuginfo.3
.nf
.B #include <elfutils/debuginfod.h>
.PP
-.BI "int debuginfod_find_debuginfo(const unsigned char *" build_id ", int " build_id_len ", char ** " path ");"
-.BI "int debuginfod_find_executable(const unsigned char *" build_id ", int " build_id_len ", char ** " path ");"
-.BI "int debuginfod_find_source(const unsigned char *" build_id ", int " build_id_len ", const char *" filename ", char ** " path ");"
-.BI "typedef int (*debuginfo_progressfn_t)(long a, long b);"
-.BI "debuginfo_progressfn_t debuginfod_set_progressfn(debuginfo_progressfn_t " progressfn ");"
+.BI "debuginfod_client *debuginfod_begin(void);"
+.BI "void debuginfod_end(debuginfod_client *" client ");"
+
+.BI "int debuginfod_find_debuginfo(debuginfod_client *" client ","
+.BI " const unsigned char *" build_id ","
+.BI " int " build_id_len ","
+.BI " char ** " path ");"
+.BI "int debuginfod_find_executable(debuginfod_client *" client ","
+.BI " const unsigned char *" build_id ","
+.BI " int " build_id_len ","
+.BI " char ** " path ");"
+.BI "int debuginfod_find_source(debuginfod_client *" client ","
+.BI " const unsigned char *" build_id ","
+.BI " int " build_id_len ","
+.BI " const char *" filename ","
+.BI " char ** " path ");"
+
+.BI "typedef int (*debuginfo_progressfn_t)(debuginfod_client *" client ","
+.BI " long a, long b);"
+.BI "void debuginfod_set_progressfn(debuginfod_client *" client ","
+.BI " debuginfo_progressfn_t " progressfn ");"
Link with \fB-ldebuginfod\fP.
.SH DESCRIPTION
+
+.BR debuginfod_begin ()
+creates a \fBdebuginfod_client\fP connection handle that should be used
+with all other calls.
+.BR debuginfod_end ()
+should be called on the \fBclient\fP handle to release all state and
+storage when done.
+
.BR debuginfod_find_debuginfo (),
.BR debuginfod_find_executable (),
and
as a debuginfod server begins transferring the target file all of the
connections to the other servers are closed.
-These functions are MT-safe.
+A \fBclient\fP handle should be used from only one thread at a time.
.SH "RETURN VALUE"
+
+\fBdebuginfod_begin\fP returns the \fBdebuginfod_client\fP handle to
+use with all other calls. On error \fBNULL\fP will be returned and
+\fBerrno\fP will be set.
+
If a find family function is successful, the resulting file is saved
to the client cache and a file descriptor to that file is returned.
The caller needs to \fBclose\fP() this descriptor. Otherwise, a
.SH "PROGRESS CALLBACK"
-As the \fBdebuginfod_find_*\fP() functions may block for seconds or longer, a progress
-callback function is called periodically, if configured with
+As the \fBdebuginfod_find_*\fP() functions may block for seconds or
+longer, a progress callback function is called periodically, if
+configured with
.BR debuginfod_set_progressfn ().
-This function sets a new progress callback function (or NULL) and
-returns the previously set function (or NULL). This function may be
-MT-unsafe.
+This function sets a new progress callback function (or NULL) for the
+client handle.
The given callback function is called from the context of each thread
that is invoking any of the other lookup functions. It is given two
link_map.c core-file.c open.c image-header.c \
dwfl_frame.c frame_unwind.c dwfl_frame_pc.c \
linux-pid-attach.c linux-core-attach.c dwfl_frame_regs.c \
- gzip.c
+ gzip.c debuginfod-client.c
if BZLIB
libdwfl_a_SOURCES += bzip2.c
--- /dev/null
+/* Try to get an ELF or debug file through the debuginfod.
+ Copyright (C) 2019 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * 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
+
+ or both in parallel, as here.
+
+ elfutils 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 copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include <dlfcn.h>
+
+static __typeof__ (debuginfod_begin) *fp_debuginfod_begin;
+static __typeof__ (debuginfod_find_executable) *fp_debuginfod_find_executable;
+static __typeof__ (debuginfod_find_debuginfo) *fp_debuginfod_find_debuginfo;
+static __typeof__ (debuginfod_end) *fp_debuginfod_end;
+
+/* NB: this is slightly thread-unsafe */
+
+static debuginfod_client *
+get_client (Dwfl *dwfl)
+{
+ if (dwfl->debuginfod != NULL)
+ return dwfl->debuginfod;
+
+ if (fp_debuginfod_begin != NULL)
+ {
+ dwfl->debuginfod = (*fp_debuginfod_begin) ();
+ return dwfl->debuginfod;
+ }
+
+ return NULL;
+}
+
+int
+__libdwfl_debuginfod_find_executable (Dwfl *dwfl,
+ const unsigned char *build_id_bits,
+ size_t build_id_len)
+{
+ int fd = -1;
+ if (build_id_len > 0)
+ {
+ debuginfod_client *c = get_client (dwfl);
+ if (c != NULL)
+ fd = (*fp_debuginfod_find_executable) (c, build_id_bits,
+ build_id_len, NULL);
+ }
+
+ return fd;
+}
+
+int
+__libdwfl_debuginfod_find_debuginfo (Dwfl *dwfl,
+ const unsigned char *build_id_bits,
+ size_t build_id_len)
+{
+ int fd = -1;
+ if (build_id_len > 0)
+ {
+ debuginfod_client *c = get_client (dwfl);
+ if (c != NULL)
+ fd = (*fp_debuginfod_find_debuginfo) (c, build_id_bits,
+ build_id_len, NULL);
+ }
+
+ return fd;
+}
+
+void
+__libdwfl_debuginfod_end (debuginfod_client *c)
+{
+ if (c != NULL)
+ (*fp_debuginfod_end) (c);
+}
+
+/* Try to get the libdebuginfod library functions to make sure
+ everything is initialized early. */
+void __attribute__ ((constructor))
+__libdwfl_debuginfod_init (void)
+{
+ void *debuginfod_so = dlopen("libdebuginfod-" VERSION ".so", RTLD_LAZY);
+
+ if (debuginfod_so == NULL)
+ debuginfod_so = dlopen("libdebuginfod.so", RTLD_LAZY);
+
+ if (debuginfod_so != NULL)
+ {
+ fp_debuginfod_begin = dlsym (debuginfod_so, "debuginfod_begin");
+ fp_debuginfod_find_executable = dlsym (debuginfod_so,
+ "debuginfod_find_executable");
+ fp_debuginfod_find_debuginfo = dlsym (debuginfod_so,
+ "debuginfod_find_debuginfo");
+ fp_debuginfod_end = dlsym (debuginfod_so, "debuginfod_end");
+
+ /* We either get them all, or we get none. */
+ if (fp_debuginfod_begin == NULL
+ || fp_debuginfod_find_executable == NULL
+ || fp_debuginfod_find_debuginfo == NULL
+ || fp_debuginfod_end == NULL)
+ {
+ fp_debuginfod_begin = NULL;
+ fp_debuginfod_find_executable = NULL;
+ fp_debuginfod_find_debuginfo = NULL;
+ fp_debuginfod_end = NULL;
+ dlclose (debuginfod_so);
+ }
+ }
+}
#include <inttypes.h>
#include <fcntl.h>
#include <unistd.h>
-#include <dlfcn.h>
#include "system.h"
-#include "debuginfod.h"
int
free (*file_name);
*file_name = NULL;
}
- else {
- /* NB: this is slightly thread-unsafe */
- static __typeof__ (debuginfod_find_executable) *fp_debuginfod_find_executable;
-
- if (fp_debuginfod_find_executable == NULL)
- {
- void *debuginfod_so = dlopen("libdebuginfod-" VERSION ".so", RTLD_LAZY);
- if (debuginfod_so == NULL)
- debuginfod_so = dlopen("libdebuginfod.so", RTLD_LAZY);
- if (debuginfod_so != NULL)
- fp_debuginfod_find_executable = dlsym (debuginfod_so, "debuginfod_find_executable");
- if (fp_debuginfod_find_executable == NULL)
- fp_debuginfod_find_executable = (void *) -1; /* never try again */
- }
-
- if (fp_debuginfod_find_executable != NULL && fp_debuginfod_find_executable != (void *) -1)
- {
- /* If all else fails and a build-id is available, query the
- debuginfo-server if enabled. */
- if (fd < 0 && mod->build_id_len > 0)
- fd = (*fp_debuginfod_find_executable) (mod->build_id_bits,
- mod->build_id_len,
- NULL);
- }
- }
+ else
+ {
+ /* If all else fails and a build-id is available, query the
+ debuginfo-server if enabled. */
+ if (fd < 0 && mod->build_id_len > 0)
+ fd = __libdwfl_debuginfod_find_executable (mod->dwfl,
+ mod->build_id_bits,
+ mod->build_id_len);
+ }
if (fd < 0 && errno == 0 && mod->build_id_len > 0)
/* Setting this with no file yet loaded is a marker that
if (dwfl == NULL)
return;
+ __libdwfl_debuginfod_end (dwfl->debuginfod);
+
if (dwfl->process)
__libdwfl_process_free (dwfl->process);
#endif
#include "libdwflP.h"
-#include "debuginfod.h"
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
-#include <dlfcn.h>
#include <sys/stat.h>
#include "system.h"
free (canon);
}
- {
- /* NB: this is slightly thread-unsafe */
- static __typeof__ (debuginfod_find_debuginfo) *fp_debuginfod_find_debuginfo;
-
- if (fp_debuginfod_find_debuginfo == NULL)
- {
- void *debuginfod_so = dlopen("libdebuginfod-" VERSION ".so", RTLD_LAZY);
- if (debuginfod_so == NULL)
- debuginfod_so = dlopen("libdebuginfod.so", RTLD_LAZY);
- if (debuginfod_so != NULL)
- fp_debuginfod_find_debuginfo = dlsym (debuginfod_so, "debuginfod_find_debuginfo");
- if (fp_debuginfod_find_debuginfo == NULL)
- fp_debuginfod_find_debuginfo = (void *) -1; /* never try again */
- }
-
- if (fp_debuginfod_find_debuginfo != NULL && fp_debuginfod_find_debuginfo != (void *) -1)
- {
- /* If all else fails and a build-id is available, query the
- debuginfo-server if enabled. */
- if (fd < 0 && bits_len > 0)
- fd = (*fp_debuginfod_find_debuginfo) (bits, bits_len, NULL);
- }
- }
+ if (fd < 0 && bits_len > 0)
+ fd = __libdwfl_debuginfod_find_debuginfo (mod->dwfl, bits, bits_len);
return fd;
}
#include "../libdw/libdwP.h" /* We need its INTDECLs. */
#include "../libdwelf/libdwelfP.h"
+#include "../debuginfod/debuginfod.h"
typedef struct Dwfl_Process Dwfl_Process;
struct Dwfl
{
const Dwfl_Callbacks *callbacks;
+ debuginfod_client *debuginfod;
Dwfl_Module *modulelist; /* List in order used by full traversals. */
extern bool __libdwfl_dynamic_vaddr_get (Elf *elf, GElf_Addr *vaddrp)
internal_function;
+/* Internal interface to libdebuginfod (if installed). */
+int
+__libdwfl_debuginfod_find_executable (Dwfl *dwfl,
+ const unsigned char *build_id_bits,
+ size_t build_id_len);
+int
+__libdwfl_debuginfod_find_debuginfo (Dwfl *dwfl,
+ const unsigned char *build_id_bits,
+ size_t build_id_len);
+void
+__libdwfl_debuginfod_end (debuginfod_client *c);
+
+
/* These are working nicely for --core, but are not ready to be
exported interfaces quite yet. */