|
Packit Service |
97d2fb |
/* Retrieve ELF / DWARF / source files from the debuginfod.
|
|
Packit Service |
97d2fb |
Copyright (C) 2019-2020 Red Hat, Inc.
|
|
Packit Service |
97d2fb |
This file is part of elfutils.
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
This file is free software; you can redistribute it and/or modify
|
|
Packit Service |
97d2fb |
it under the terms of either
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
* the GNU Lesser General Public License as published by the Free
|
|
Packit Service |
97d2fb |
Software Foundation; either version 3 of the License, or (at
|
|
Packit Service |
97d2fb |
your option) any later version
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
or
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
* the GNU General Public License as published by the Free
|
|
Packit Service |
97d2fb |
Software Foundation; either version 2 of the License, or (at
|
|
Packit Service |
97d2fb |
your option) any later version
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
or both in parallel, as here.
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
elfutils is distributed in the hope that it will be useful, but
|
|
Packit Service |
97d2fb |
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
97d2fb |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit Service |
97d2fb |
General Public License for more details.
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
You should have received copies of the GNU General Public License and
|
|
Packit Service |
97d2fb |
the GNU Lesser General Public License along with this program. If
|
|
Packit Service |
97d2fb |
not, see <http://www.gnu.org/licenses/>. */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* cargo-cult from libdwfl linux-kernel-modules.c */
|
|
Packit Service |
97d2fb |
/* In case we have a bad fts we include this before config.h because it
|
|
Packit Service |
97d2fb |
can't handle _FILE_OFFSET_BITS.
|
|
Packit Service |
97d2fb |
Everything we need here is fine if its declarations just come first.
|
|
Packit Service |
97d2fb |
Also, include sys/types.h before fts. On some systems fts.h is not self
|
|
Packit Service |
97d2fb |
contained. */
|
|
Packit Service |
97d2fb |
#ifdef BAD_FTS
|
|
Packit Service |
97d2fb |
#include <sys/types.h>
|
|
Packit Service |
97d2fb |
#include <fts.h>
|
|
Packit Service |
97d2fb |
#endif
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
#include "config.h"
|
|
Packit Service |
97d2fb |
#include "debuginfod.h"
|
|
Packit Service |
97d2fb |
#include "system.h"
|
|
Packit Service |
97d2fb |
#include <errno.h>
|
|
Packit Service |
97d2fb |
#include <stdlib.h>
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* We might be building a bootstrap dummy library, which is really simple. */
|
|
Packit Service |
97d2fb |
#ifdef DUMMY_LIBDEBUGINFOD
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
debuginfod_client *debuginfod_begin (void) { errno = ENOSYS; return NULL; }
|
|
Packit Service |
97d2fb |
int debuginfod_find_debuginfo (debuginfod_client *c, const unsigned char *b,
|
|
Packit Service |
97d2fb |
int s, char **p) { return -ENOSYS; }
|
|
Packit Service |
97d2fb |
int debuginfod_find_executable (debuginfod_client *c, const unsigned char *b,
|
|
Packit Service |
97d2fb |
int s, char **p) { return -ENOSYS; }
|
|
Packit Service |
97d2fb |
int debuginfod_find_source (debuginfod_client *c, const unsigned char *b,
|
|
Packit Service |
97d2fb |
int s, const char *f, char **p) { return -ENOSYS; }
|
|
Packit Service |
97d2fb |
void debuginfod_set_progressfn(debuginfod_client *c,
|
|
Packit Service |
97d2fb |
debuginfod_progressfn_t fn) { }
|
|
Packit Service |
97d2fb |
void debuginfod_set_user_data (debuginfod_client *c, void *d) { }
|
|
Packit Service |
97d2fb |
void* debuginfod_get_user_data (debuginfod_client *c) { return NULL; }
|
|
Packit Service |
97d2fb |
const char* debuginfod_get_url (debuginfod_client *c) { return NULL; }
|
|
Packit Service |
97d2fb |
int debuginfod_add_http_header (debuginfod_client *c,
|
|
Packit Service |
97d2fb |
const char *h) { return -ENOSYS; }
|
|
Packit Service |
97d2fb |
void debuginfod_end (debuginfod_client *c) { }
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
#else /* DUMMY_LIBDEBUGINFOD */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
#include <assert.h>
|
|
Packit Service |
97d2fb |
#include <dirent.h>
|
|
Packit Service |
97d2fb |
#include <stdio.h>
|
|
Packit Service |
97d2fb |
#include <errno.h>
|
|
Packit Service |
97d2fb |
#include <unistd.h>
|
|
Packit Service |
97d2fb |
#include <fcntl.h>
|
|
Packit Service |
97d2fb |
#include <fts.h>
|
|
Packit Service |
97d2fb |
#include <regex.h>
|
|
Packit Service |
97d2fb |
#include <string.h>
|
|
Packit Service |
97d2fb |
#include <stdbool.h>
|
|
Packit Service |
97d2fb |
#include <linux/limits.h>
|
|
Packit Service |
97d2fb |
#include <time.h>
|
|
Packit Service |
97d2fb |
#include <utime.h>
|
|
Packit Service |
97d2fb |
#include <sys/syscall.h>
|
|
Packit Service |
97d2fb |
#include <sys/types.h>
|
|
Packit Service |
97d2fb |
#include <sys/stat.h>
|
|
Packit Service |
97d2fb |
#include <sys/utsname.h>
|
|
Packit Service |
97d2fb |
#include <curl/curl.h>
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* If fts.h is included before config.h, its indirect inclusions may not
|
|
Packit Service |
97d2fb |
give us the right LFS aliases of these functions, so map them manually. */
|
|
Packit Service |
97d2fb |
#ifdef BAD_FTS
|
|
Packit Service |
97d2fb |
#ifdef _FILE_OFFSET_BITS
|
|
Packit Service |
97d2fb |
#define open open64
|
|
Packit Service |
97d2fb |
#define fopen fopen64
|
|
Packit Service |
97d2fb |
#endif
|
|
Packit Service |
97d2fb |
#else
|
|
Packit Service |
97d2fb |
#include <sys/types.h>
|
|
Packit Service |
97d2fb |
#include <fts.h>
|
|
Packit Service |
97d2fb |
#endif
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
struct debuginfod_client
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Progress/interrupt callback function. */
|
|
Packit Service |
97d2fb |
debuginfod_progressfn_t progressfn;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Stores user data. */
|
|
Packit Service |
97d2fb |
void* user_data;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Stores current/last url, if any. */
|
|
Packit Service |
97d2fb |
char* url;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Accumulates outgoing http header names/values. */
|
|
Packit Service |
97d2fb |
int user_agent_set_p; /* affects add_default_headers */
|
|
Packit Service |
97d2fb |
struct curl_slist *headers;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Flags the default_progressfn having printed something that
|
|
Packit Service |
97d2fb |
debuginfod_end needs to terminate. */
|
|
Packit Service |
97d2fb |
int default_progressfn_printed_p;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Can contain all other context, like cache_path, server_urls,
|
|
Packit Service |
97d2fb |
timeout or other info gotten from environment variables, the
|
|
Packit Service |
97d2fb |
handle data, etc. So those don't have to be reparsed and
|
|
Packit Service |
97d2fb |
recreated on each request. */
|
|
Packit Service |
97d2fb |
};
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* The cache_clean_interval_s file within the debuginfod cache specifies
|
|
Packit Service |
97d2fb |
how frequently the cache should be cleaned. The file's st_mtime represents
|
|
Packit Service |
97d2fb |
the time of last cleaning. */
|
|
Packit Service |
97d2fb |
static const char *cache_clean_interval_filename = "cache_clean_interval_s";
|
|
Packit Service |
97d2fb |
static const time_t cache_clean_default_interval_s = 86400; /* 1 day */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* The cache_max_unused_age_s file within the debuginfod cache specifies the
|
|
Packit Service |
97d2fb |
the maximum time since last access that a file will remain in the cache. */
|
|
Packit Service |
97d2fb |
static const char *cache_max_unused_age_filename = "max_unused_age_s";
|
|
Packit Service |
97d2fb |
static const time_t cache_default_max_unused_age_s = 604800; /* 1 week */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Location of the cache of files downloaded from debuginfods.
|
|
Packit Service |
97d2fb |
The default parent directory is $HOME, or '/' if $HOME doesn't exist. */
|
|
Packit Service |
97d2fb |
static const char *cache_default_name = ".debuginfod_client_cache";
|
|
Packit Service |
97d2fb |
static const char *cache_xdg_name = "debuginfod_client";
|
|
Packit Service |
97d2fb |
static const char *cache_path_envvar = DEBUGINFOD_CACHE_PATH_ENV_VAR;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* URLs of debuginfods, separated by url_delim. */
|
|
Packit Service |
97d2fb |
static const char *server_urls_envvar = DEBUGINFOD_URLS_ENV_VAR;
|
|
Packit Service |
97d2fb |
static const char *url_delim = " ";
|
|
Packit Service |
97d2fb |
static const char url_delim_char = ' ';
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Timeout for debuginfods, in seconds (to get at least 100K). */
|
|
Packit Service |
97d2fb |
static const char *server_timeout_envvar = DEBUGINFOD_TIMEOUT_ENV_VAR;
|
|
Packit Service |
97d2fb |
static const long default_timeout = 90;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Data associated with a particular CURL easy handle. Passed to
|
|
Packit Service |
97d2fb |
the write callback. */
|
|
Packit Service |
97d2fb |
struct handle_data
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Cache file to be written to in case query is successful. */
|
|
Packit Service |
97d2fb |
int fd;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* URL queried by this handle. */
|
|
Packit Service |
97d2fb |
char url[PATH_MAX];
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* This handle. */
|
|
Packit Service |
97d2fb |
CURL *handle;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* The client object whom we're serving. */
|
|
Packit Service |
97d2fb |
debuginfod_client *client;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Pointer to handle that should write to fd. Initially points to NULL,
|
|
Packit Service |
97d2fb |
then points to the first handle that begins writing the target file
|
|
Packit Service |
97d2fb |
to the cache. Used to ensure that a file is not downloaded from
|
|
Packit Service |
97d2fb |
multiple servers unnecessarily. */
|
|
Packit Service |
97d2fb |
CURL **target_handle;
|
|
Packit Service |
97d2fb |
};
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
static size_t
|
|
Packit Service |
97d2fb |
debuginfod_write_callback (char *ptr, size_t size, size_t nmemb, void *data)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
ssize_t count = size * nmemb;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
struct handle_data *d = (struct handle_data*)data;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Indicate to other handles that they can abort their transfer. */
|
|
Packit Service |
97d2fb |
if (*d->target_handle == NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
*d->target_handle = d->handle;
|
|
Packit Service |
97d2fb |
/* update the client object */
|
|
Packit Service |
97d2fb |
const char *url = NULL;
|
|
Packit Service |
97d2fb |
(void) curl_easy_getinfo (d->handle, CURLINFO_EFFECTIVE_URL, &url;;
|
|
Packit Service |
97d2fb |
if (url)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
free (d->client->url);
|
|
Packit Service |
97d2fb |
d->client->url = strdup(url); /* ok if fails */
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* If this handle isn't the target handle, abort transfer. */
|
|
Packit Service |
97d2fb |
if (*d->target_handle != d->handle)
|
|
Packit Service |
97d2fb |
return -1;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
return (size_t) write(d->fd, (void*)ptr, count);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Create the cache and interval file if they do not already exist.
|
|
Packit Service |
97d2fb |
Return 0 if cache and config file are initialized, otherwise return
|
|
Packit Service |
97d2fb |
the appropriate error code. */
|
|
Packit Service |
97d2fb |
static int
|
|
Packit Service |
97d2fb |
debuginfod_init_cache (char *cache_path, char *interval_path, char *maxage_path)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
struct stat st;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* If the cache and config file already exist then we are done. */
|
|
Packit Service |
97d2fb |
if (stat(cache_path, &st) == 0 && stat(interval_path, &st) == 0)
|
|
Packit Service |
97d2fb |
return 0;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Create the cache and config files as necessary. */
|
|
Packit Service |
97d2fb |
if (stat(cache_path, &st) != 0 && mkdir(cache_path, 0777) < 0)
|
|
Packit Service |
97d2fb |
return -errno;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
int fd = -1;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* init cleaning interval config file. */
|
|
Packit Service |
97d2fb |
fd = open(interval_path, O_CREAT | O_RDWR, 0666);
|
|
Packit Service |
97d2fb |
if (fd < 0)
|
|
Packit Service |
97d2fb |
return -errno;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (dprintf(fd, "%ld", cache_clean_default_interval_s) < 0)
|
|
Packit Service |
97d2fb |
return -errno;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* init max age config file. */
|
|
Packit Service |
97d2fb |
if (stat(maxage_path, &st) != 0
|
|
Packit Service |
97d2fb |
&& (fd = open(maxage_path, O_CREAT | O_RDWR, 0666)) < 0)
|
|
Packit Service |
97d2fb |
return -errno;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (dprintf(fd, "%ld", cache_default_max_unused_age_s) < 0)
|
|
Packit Service |
97d2fb |
return -errno;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
return 0;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Delete any files that have been unmodied for a period
|
|
Packit Service |
97d2fb |
longer than $DEBUGINFOD_CACHE_CLEAN_INTERVAL_S. */
|
|
Packit Service |
97d2fb |
static int
|
|
Packit Service |
97d2fb |
debuginfod_clean_cache(debuginfod_client *c,
|
|
Packit Service |
97d2fb |
char *cache_path, char *interval_path,
|
|
Packit Service |
97d2fb |
char *max_unused_path)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
struct stat st;
|
|
Packit Service |
97d2fb |
FILE *interval_file;
|
|
Packit Service |
97d2fb |
FILE *max_unused_file;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (stat(interval_path, &st) == -1)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Create new interval file. */
|
|
Packit Service |
97d2fb |
interval_file = fopen(interval_path, "w");
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (interval_file == NULL)
|
|
Packit Service |
97d2fb |
return -errno;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
int rc = fprintf(interval_file, "%ld", cache_clean_default_interval_s);
|
|
Packit Service |
97d2fb |
fclose(interval_file);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (rc < 0)
|
|
Packit Service |
97d2fb |
return -errno;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Check timestamp of interval file to see whether cleaning is necessary. */
|
|
Packit Service |
97d2fb |
time_t clean_interval;
|
|
Packit Service |
97d2fb |
interval_file = fopen(interval_path, "r");
|
|
Packit Service |
97d2fb |
if (interval_file)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (fscanf(interval_file, "%ld", &clean_interval) != 1)
|
|
Packit Service |
97d2fb |
clean_interval = cache_clean_default_interval_s;
|
|
Packit Service |
97d2fb |
fclose(interval_file);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
clean_interval = cache_clean_default_interval_s;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (time(NULL) - st.st_mtime < clean_interval)
|
|
Packit Service |
97d2fb |
/* Interval has not passed, skip cleaning. */
|
|
Packit Service |
97d2fb |
return 0;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Read max unused age value from config file. */
|
|
Packit Service |
97d2fb |
time_t max_unused_age;
|
|
Packit Service |
97d2fb |
max_unused_file = fopen(max_unused_path, "r");
|
|
Packit Service |
97d2fb |
if (max_unused_file)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (fscanf(max_unused_file, "%ld", &max_unused_age) != 1)
|
|
Packit Service |
97d2fb |
max_unused_age = cache_default_max_unused_age_s;
|
|
Packit Service |
97d2fb |
fclose(max_unused_file);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
max_unused_age = cache_default_max_unused_age_s;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
char * const dirs[] = { cache_path, NULL, };
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
FTS *fts = fts_open(dirs, 0, NULL);
|
|
Packit Service |
97d2fb |
if (fts == NULL)
|
|
Packit Service |
97d2fb |
return -errno;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
regex_t re;
|
|
Packit Service |
97d2fb |
const char * pattern = ".*/[a-f0-9]+/(debuginfo|executable|source.*)$";
|
|
Packit Service |
97d2fb |
if (regcomp (&re, pattern, REG_EXTENDED | REG_NOSUB) != 0)
|
|
Packit Service |
97d2fb |
return -ENOMEM;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
FTSENT *f;
|
|
Packit Service |
97d2fb |
long files = 0;
|
|
Packit Service |
97d2fb |
while ((f = fts_read(fts)) != NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* ignore any files that do not match the pattern. */
|
|
Packit Service |
97d2fb |
if (regexec (&re, f->fts_path, 0, NULL, 0) != 0)
|
|
Packit Service |
97d2fb |
continue;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
files++;
|
|
Packit Service |
97d2fb |
if (c->progressfn) /* inform/check progress callback */
|
|
Packit Service |
97d2fb |
if ((c->progressfn) (c, files, 0))
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
switch (f->fts_info)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
case FTS_F:
|
|
Packit Service |
97d2fb |
/* delete file if max_unused_age has been met or exceeded. */
|
|
Packit Service |
97d2fb |
/* XXX consider extra effort to clean up old tmp files */
|
|
Packit Service |
97d2fb |
if (time(NULL) - f->fts_statp->st_atime >= max_unused_age)
|
|
Packit Service |
97d2fb |
unlink (f->fts_path);
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
case FTS_DP:
|
|
Packit Service |
97d2fb |
/* Remove if empty. */
|
|
Packit Service |
97d2fb |
(void) rmdir (f->fts_path);
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
default:
|
|
Packit Service |
97d2fb |
;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
fts_close (fts);
|
|
Packit Service |
97d2fb |
regfree (&re);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Update timestamp representing when the cache was last cleaned. */
|
|
Packit Service |
97d2fb |
utime (interval_path, NULL);
|
|
Packit Service |
97d2fb |
return 0;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
#define MAX_BUILD_ID_BYTES 64
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
static void
|
|
Packit Service |
97d2fb |
add_default_headers(debuginfod_client *client)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (client->user_agent_set_p)
|
|
Packit Service |
97d2fb |
return;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Compute a User-Agent: string to send. The more accurately this
|
|
Packit Service |
97d2fb |
describes this host, the likelier that the debuginfod servers
|
|
Packit Service |
97d2fb |
might be able to locate debuginfo for us. */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
char* utspart = NULL;
|
|
Packit Service |
97d2fb |
struct utsname uts;
|
|
Packit Service |
97d2fb |
int rc = 0;
|
|
Packit Service |
97d2fb |
rc = uname (&uts;;
|
|
Packit Service |
97d2fb |
if (rc == 0)
|
|
Packit Service |
97d2fb |
rc = asprintf(& utspart, "%s/%s", uts.sysname, uts.machine);
|
|
Packit Service |
97d2fb |
if (rc < 0)
|
|
Packit Service |
97d2fb |
utspart = NULL;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
FILE *f = fopen ("/etc/os-release", "r");
|
|
Packit Service |
97d2fb |
if (f == NULL)
|
|
Packit Service |
97d2fb |
f = fopen ("/usr/lib/os-release", "r");
|
|
Packit Service |
97d2fb |
char *id = NULL;
|
|
Packit Service |
97d2fb |
char *version = NULL;
|
|
Packit Service |
97d2fb |
if (f != NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
while (id == NULL || version == NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
char buf[128];
|
|
Packit Service |
97d2fb |
char *s = &buf[0];
|
|
Packit Service |
97d2fb |
if (fgets (s, sizeof(buf), f) == NULL)
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
int len = strlen (s);
|
|
Packit Service |
97d2fb |
if (len < 3)
|
|
Packit Service |
97d2fb |
continue;
|
|
Packit Service |
97d2fb |
if (s[len - 1] == '\n')
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
s[len - 1] = '\0';
|
|
Packit Service |
97d2fb |
len--;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
char *v = strchr (s, '=');
|
|
Packit Service |
97d2fb |
if (v == NULL || strlen (v) < 2)
|
|
Packit Service |
97d2fb |
continue;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Split var and value. */
|
|
Packit Service |
97d2fb |
*v = '\0';
|
|
Packit Service |
97d2fb |
v++;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Remove optional quotes around value string. */
|
|
Packit Service |
97d2fb |
if (*v == '"' || *v == '\'')
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
v++;
|
|
Packit Service |
97d2fb |
s[len - 1] = '\0';
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
if (strcmp (s, "ID") == 0)
|
|
Packit Service |
97d2fb |
id = strdup (v);
|
|
Packit Service |
97d2fb |
if (strcmp (s, "VERSION_ID") == 0)
|
|
Packit Service |
97d2fb |
version = strdup (v);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
fclose (f);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
char *ua = NULL;
|
|
Packit Service |
97d2fb |
rc = asprintf(& ua, "User-Agent: %s/%s,%s,%s/%s",
|
|
Packit Service |
97d2fb |
PACKAGE_NAME, PACKAGE_VERSION,
|
|
Packit Service |
97d2fb |
utspart ?: "",
|
|
Packit Service |
97d2fb |
id ?: "",
|
|
Packit Service |
97d2fb |
version ?: "");
|
|
Packit Service |
97d2fb |
if (rc < 0)
|
|
Packit Service |
97d2fb |
ua = NULL;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (ua)
|
|
Packit Service |
97d2fb |
(void) debuginfod_add_http_header (client, ua);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
free (ua);
|
|
Packit Service |
97d2fb |
free (id);
|
|
Packit Service |
97d2fb |
free (version);
|
|
Packit Service |
97d2fb |
free (utspart);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
#define xalloc_str(p, fmt, args...) \
|
|
Packit Service |
97d2fb |
do \
|
|
Packit Service |
97d2fb |
{ \
|
|
Packit Service |
97d2fb |
if (asprintf (&p, fmt, args) < 0) \
|
|
Packit Service |
97d2fb |
{ \
|
|
Packit Service |
97d2fb |
p = NULL; \
|
|
Packit Service |
97d2fb |
rc = -ENOMEM; \
|
|
Packit Service |
97d2fb |
goto out; \
|
|
Packit Service |
97d2fb |
} \
|
|
Packit Service |
97d2fb |
} while (0)
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Offer a basic form of progress tracing */
|
|
Packit Service |
97d2fb |
static int
|
|
Packit Service |
97d2fb |
default_progressfn (debuginfod_client *c, long a, long b)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
const char* url = debuginfod_get_url (c);
|
|
Packit Service |
97d2fb |
int len = 0;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* We prefer to print the host part of the URL to keep the
|
|
Packit Service |
97d2fb |
message short. */
|
|
Packit Service |
97d2fb |
if (url != NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
const char* buildid = strstr(url, "buildid/");
|
|
Packit Service |
97d2fb |
if (buildid != NULL)
|
|
Packit Service |
97d2fb |
len = (buildid - url);
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
len = strlen(url);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (b == 0 || url==NULL) /* early stage */
|
|
Packit Service |
97d2fb |
dprintf(STDERR_FILENO,
|
|
Packit Service |
97d2fb |
"\rDownloading %c", "-/|\\"[a % 4]);
|
|
Packit Service |
97d2fb |
else if (b < 0) /* download in progress but unknown total length */
|
|
Packit Service |
97d2fb |
dprintf(STDERR_FILENO,
|
|
Packit Service |
97d2fb |
"\rDownloading from %.*s %ld",
|
|
Packit Service |
97d2fb |
len, url, a);
|
|
Packit Service |
97d2fb |
else /* download in progress, and known total length */
|
|
Packit Service |
97d2fb |
dprintf(STDERR_FILENO,
|
|
Packit Service |
97d2fb |
"\rDownloading from %.*s %ld/%ld",
|
|
Packit Service |
97d2fb |
len, url, a, b);
|
|
Packit Service |
97d2fb |
c->default_progressfn_printed_p = 1;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
return 0;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Query each of the server URLs found in $DEBUGINFOD_URLS for the file
|
|
Packit Service |
97d2fb |
with the specified build-id, type (debuginfo, executable or source)
|
|
Packit Service |
97d2fb |
and filename. filename may be NULL. If found, return a file
|
|
Packit Service |
97d2fb |
descriptor for the target, otherwise return an error code.
|
|
Packit Service |
97d2fb |
*/
|
|
Packit Service |
97d2fb |
static int
|
|
Packit Service |
97d2fb |
debuginfod_query_server (debuginfod_client *c,
|
|
Packit Service |
97d2fb |
const unsigned char *build_id,
|
|
Packit Service |
97d2fb |
int build_id_len,
|
|
Packit Service |
97d2fb |
const char *type,
|
|
Packit Service |
97d2fb |
const char *filename,
|
|
Packit Service |
97d2fb |
char **path)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
char *server_urls;
|
|
Packit Service |
97d2fb |
char *urls_envvar;
|
|
Packit Service |
97d2fb |
char *cache_path = NULL;
|
|
Packit Service |
97d2fb |
char *maxage_path = NULL;
|
|
Packit Service |
97d2fb |
char *interval_path = NULL;
|
|
Packit Service |
97d2fb |
char *target_cache_dir = NULL;
|
|
Packit Service |
97d2fb |
char *target_cache_path = NULL;
|
|
Packit Service |
97d2fb |
char *target_cache_tmppath = NULL;
|
|
Packit Service |
97d2fb |
char suffix[PATH_MAX + 1]; /* +1 for zero terminator. */
|
|
Packit Service |
97d2fb |
char build_id_bytes[MAX_BUILD_ID_BYTES * 2 + 1];
|
|
Packit Service |
97d2fb |
int rc;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Clear the obsolete URL from a previous _find operation. */
|
|
Packit Service |
97d2fb |
free (c->url);
|
|
Packit Service |
97d2fb |
c->url = NULL;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
add_default_headers(c);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Is there any server we can query? If not, don't do any work,
|
|
Packit Service |
97d2fb |
just return with ENOSYS. Don't even access the cache. */
|
|
Packit Service |
97d2fb |
urls_envvar = getenv(server_urls_envvar);
|
|
Packit Service |
97d2fb |
if (urls_envvar == NULL || urls_envvar[0] == '\0')
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
rc = -ENOSYS;
|
|
Packit Service |
97d2fb |
goto out;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Copy lowercase hex representation of build_id into buf. */
|
|
Packit Service |
97d2fb |
if ((build_id_len >= MAX_BUILD_ID_BYTES) ||
|
|
Packit Service |
97d2fb |
(build_id_len == 0 &&
|
|
Packit Service |
97d2fb |
strlen ((const char *) build_id) > MAX_BUILD_ID_BYTES*2))
|
|
Packit Service |
97d2fb |
return -EINVAL;
|
|
Packit Service |
97d2fb |
if (build_id_len == 0) /* expect clean hexadecimal */
|
|
Packit Service |
97d2fb |
strcpy (build_id_bytes, (const char *) build_id);
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
for (int i = 0; i < build_id_len; i++)
|
|
Packit Service |
97d2fb |
sprintf(build_id_bytes + (i * 2), "%02x", build_id[i]);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (filename != NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (filename[0] != '/') // must start with /
|
|
Packit Service |
97d2fb |
return -EINVAL;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* copy the filename to suffix, s,/,#,g */
|
|
Packit Service |
97d2fb |
unsigned q = 0;
|
|
Packit Service |
97d2fb |
for (unsigned fi=0; q < PATH_MAX-2; fi++) /* -2, escape is 2 chars. */
|
|
Packit Service |
97d2fb |
switch (filename[fi])
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
case '\0':
|
|
Packit Service |
97d2fb |
suffix[q] = '\0';
|
|
Packit Service |
97d2fb |
q = PATH_MAX-1; /* escape for loop too */
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
case '/': /* escape / to prevent dir escape */
|
|
Packit Service |
97d2fb |
suffix[q++]='#';
|
|
Packit Service |
97d2fb |
suffix[q++]='#';
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
case '#': /* escape # to prevent /# vs #/ collisions */
|
|
Packit Service |
97d2fb |
suffix[q++]='#';
|
|
Packit Service |
97d2fb |
suffix[q++]='_';
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
default:
|
|
Packit Service |
97d2fb |
suffix[q++]=filename[fi];
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
suffix[q] = '\0';
|
|
Packit Service |
97d2fb |
/* If the DWARF filenames are super long, this could exceed
|
|
Packit Service |
97d2fb |
PATH_MAX and truncate/collide. Oh well, that'll teach
|
|
Packit Service |
97d2fb |
them! */
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
suffix[0] = '\0';
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* set paths needed to perform the query
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
example format
|
|
Packit Service |
97d2fb |
cache_path: $HOME/.cache
|
|
Packit Service |
97d2fb |
target_cache_dir: $HOME/.cache/0123abcd
|
|
Packit Service |
97d2fb |
target_cache_path: $HOME/.cache/0123abcd/debuginfo
|
|
Packit Service |
97d2fb |
target_cache_path: $HOME/.cache/0123abcd/source#PATH#TO#SOURCE ?
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
$XDG_CACHE_HOME takes priority over $HOME/.cache.
|
|
Packit Service |
97d2fb |
$DEBUGINFOD_CACHE_PATH takes priority over $HOME/.cache and $XDG_CACHE_HOME.
|
|
Packit Service |
97d2fb |
*/
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Determine location of the cache. The path specified by the debuginfod
|
|
Packit Service |
97d2fb |
cache environment variable takes priority. */
|
|
Packit Service |
97d2fb |
char *cache_var = getenv(cache_path_envvar);
|
|
Packit Service |
97d2fb |
if (cache_var != NULL && strlen (cache_var) > 0)
|
|
Packit Service |
97d2fb |
xalloc_str (cache_path, "%s", cache_var);
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* If a cache already exists in $HOME ('/' if $HOME isn't set), then use
|
|
Packit Service |
97d2fb |
that. Otherwise use the XDG cache directory naming format. */
|
|
Packit Service |
97d2fb |
xalloc_str (cache_path, "%s/%s", getenv ("HOME") ?: "/", cache_default_name);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
struct stat st;
|
|
Packit Service |
97d2fb |
if (stat (cache_path, &st) < 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
char cachedir[PATH_MAX];
|
|
Packit Service |
97d2fb |
char *xdg = getenv ("XDG_CACHE_HOME");
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (xdg != NULL && strlen (xdg) > 0)
|
|
Packit Service |
97d2fb |
snprintf (cachedir, PATH_MAX, "%s", xdg);
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
snprintf (cachedir, PATH_MAX, "%s/.cache", getenv ("HOME") ?: "/");
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Create XDG cache directory if it doesn't exist. */
|
|
Packit Service |
97d2fb |
if (stat (cachedir, &st) == 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (! S_ISDIR (st.st_mode))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
rc = -EEXIST;
|
|
Packit Service |
97d2fb |
goto out;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
rc = mkdir (cachedir, 0700);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Also check for EEXIST and S_ISDIR in case another client just
|
|
Packit Service |
97d2fb |
happened to create the cache. */
|
|
Packit Service |
97d2fb |
if (rc < 0
|
|
Packit Service |
97d2fb |
&& (errno != EEXIST
|
|
Packit Service |
97d2fb |
|| stat (cachedir, &st) != 0
|
|
Packit Service |
97d2fb |
|| ! S_ISDIR (st.st_mode)))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
rc = -errno;
|
|
Packit Service |
97d2fb |
goto out;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
free (cache_path);
|
|
Packit Service |
97d2fb |
xalloc_str (cache_path, "%s/%s", cachedir, cache_xdg_name);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
xalloc_str (target_cache_dir, "%s/%s", cache_path, build_id_bytes);
|
|
Packit Service |
97d2fb |
xalloc_str (target_cache_path, "%s/%s%s", target_cache_dir, type, suffix);
|
|
Packit Service |
97d2fb |
xalloc_str (target_cache_tmppath, "%s.XXXXXX", target_cache_path);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* XXX combine these */
|
|
Packit Service |
97d2fb |
xalloc_str (interval_path, "%s/%s", cache_path, cache_clean_interval_filename);
|
|
Packit Service |
97d2fb |
xalloc_str (maxage_path, "%s/%s", cache_path, cache_max_unused_age_filename);
|
|
Packit Service |
97d2fb |
rc = debuginfod_init_cache(cache_path, interval_path, maxage_path);
|
|
Packit Service |
97d2fb |
if (rc != 0)
|
|
Packit Service |
97d2fb |
goto out;
|
|
Packit Service |
97d2fb |
rc = debuginfod_clean_cache(c, cache_path, interval_path, maxage_path);
|
|
Packit Service |
97d2fb |
if (rc != 0)
|
|
Packit Service |
97d2fb |
goto out;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* If the target is already in the cache then we are done. */
|
|
Packit Service |
97d2fb |
int fd = open (target_cache_path, O_RDONLY);
|
|
Packit Service |
97d2fb |
if (fd >= 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Success!!!! */
|
|
Packit Service |
97d2fb |
if (path != NULL)
|
|
Packit Service |
97d2fb |
*path = strdup(target_cache_path);
|
|
Packit Service |
97d2fb |
rc = fd;
|
|
Packit Service |
97d2fb |
goto out;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
long timeout = default_timeout;
|
|
Packit Service |
97d2fb |
const char* timeout_envvar = getenv(server_timeout_envvar);
|
|
Packit Service |
97d2fb |
if (timeout_envvar != NULL)
|
|
Packit Service |
97d2fb |
timeout = atoi (timeout_envvar);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* make a copy of the envvar so it can be safely modified. */
|
|
Packit Service |
97d2fb |
server_urls = strdup(urls_envvar);
|
|
Packit Service |
97d2fb |
if (server_urls == NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
rc = -ENOMEM;
|
|
Packit Service |
97d2fb |
goto out;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
/* thereafter, goto out0 on error*/
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* create target directory in cache if not found. */
|
|
Packit Service |
97d2fb |
struct stat st;
|
|
Packit Service |
97d2fb |
if (stat(target_cache_dir, &st) == -1 && mkdir(target_cache_dir, 0700) < 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
rc = -errno;
|
|
Packit Service |
97d2fb |
goto out0;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* NB: write to a temporary file first, to avoid race condition of
|
|
Packit Service |
97d2fb |
multiple clients checking the cache, while a partially-written or empty
|
|
Packit Service |
97d2fb |
file is in there, being written from libcurl. */
|
|
Packit Service |
97d2fb |
fd = mkstemp (target_cache_tmppath);
|
|
Packit Service |
97d2fb |
if (fd < 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
rc = -errno;
|
|
Packit Service |
97d2fb |
goto out0;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Count number of URLs. */
|
|
Packit Service |
97d2fb |
int num_urls = 0;
|
|
Packit Service |
97d2fb |
for (int i = 0; server_urls[i] != '\0'; i++)
|
|
Packit Service |
97d2fb |
if (server_urls[i] != url_delim_char
|
|
Packit Service |
97d2fb |
&& (i == 0 || server_urls[i - 1] == url_delim_char))
|
|
Packit Service |
97d2fb |
num_urls++;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
CURLM *curlm = curl_multi_init();
|
|
Packit Service |
97d2fb |
if (curlm == NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
rc = -ENETUNREACH;
|
|
Packit Service |
97d2fb |
goto out0;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Tracks which handle should write to fd. Set to the first
|
|
Packit Service |
97d2fb |
handle that is ready to write the target file to the cache. */
|
|
Packit Service |
97d2fb |
CURL *target_handle = NULL;
|
|
Packit Service |
97d2fb |
struct handle_data *data = malloc(sizeof(struct handle_data) * num_urls);
|
|
Packit Service |
97d2fb |
if (data == NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
rc = -ENOMEM;
|
|
Packit Service |
97d2fb |
goto out0;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* thereafter, goto out1 on error. */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Initalize handle_data with default values. */
|
|
Packit Service |
97d2fb |
for (int i = 0; i < num_urls; i++)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
data[i].handle = NULL;
|
|
Packit Service |
97d2fb |
data[i].fd = -1;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
char *strtok_saveptr;
|
|
Packit Service |
97d2fb |
char *server_url = strtok_r(server_urls, url_delim, &strtok_saveptr);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Initialize each handle. */
|
|
Packit Service |
97d2fb |
for (int i = 0; i < num_urls && server_url != NULL; i++)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
data[i].fd = fd;
|
|
Packit Service |
97d2fb |
data[i].target_handle = &target_handle;
|
|
Packit Service |
97d2fb |
data[i].handle = curl_easy_init();
|
|
Packit Service |
97d2fb |
data[i].client = c;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (data[i].handle == NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
rc = -ENETUNREACH;
|
|
Packit Service |
97d2fb |
goto out1;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Build handle url. Tolerate both http://foo:999 and
|
|
Packit Service |
97d2fb |
http://foo:999/ forms */
|
|
Packit Service |
97d2fb |
char *slashbuildid;
|
|
Packit Service |
97d2fb |
if (strlen(server_url) > 1 && server_url[strlen(server_url)-1] == '/')
|
|
Packit Service |
97d2fb |
slashbuildid = "buildid";
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
slashbuildid = "/buildid";
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (filename) /* must start with / */
|
|
Packit Service |
97d2fb |
snprintf(data[i].url, PATH_MAX, "%s%s/%s/%s%s", server_url,
|
|
Packit Service |
97d2fb |
slashbuildid, build_id_bytes, type, filename);
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
snprintf(data[i].url, PATH_MAX, "%s%s/%s/%s", server_url,
|
|
Packit Service |
97d2fb |
slashbuildid, build_id_bytes, type);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
curl_easy_setopt(data[i].handle, CURLOPT_URL, data[i].url);
|
|
Packit Service |
97d2fb |
curl_easy_setopt(data[i].handle,
|
|
Packit Service |
97d2fb |
CURLOPT_WRITEFUNCTION,
|
|
Packit Service |
97d2fb |
debuginfod_write_callback);
|
|
Packit Service |
97d2fb |
curl_easy_setopt(data[i].handle, CURLOPT_WRITEDATA, (void*)&data[i]);
|
|
Packit Service |
97d2fb |
if (timeout > 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Make sure there is at least some progress,
|
|
Packit Service |
97d2fb |
try to get at least 100K per timeout seconds. */
|
|
Packit Service |
97d2fb |
curl_easy_setopt (data[i].handle, CURLOPT_LOW_SPEED_TIME,
|
|
Packit Service |
97d2fb |
timeout);
|
|
Packit Service |
97d2fb |
curl_easy_setopt (data[i].handle, CURLOPT_LOW_SPEED_LIMIT,
|
|
Packit Service |
97d2fb |
100 * 1024L);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
curl_easy_setopt(data[i].handle, CURLOPT_FILETIME, (long) 1);
|
|
Packit Service |
97d2fb |
curl_easy_setopt(data[i].handle, CURLOPT_FOLLOWLOCATION, (long) 1);
|
|
Packit Service |
97d2fb |
curl_easy_setopt(data[i].handle, CURLOPT_FAILONERROR, (long) 1);
|
|
Packit Service |
97d2fb |
curl_easy_setopt(data[i].handle, CURLOPT_NOSIGNAL, (long) 1);
|
|
Packit Service |
97d2fb |
#if LIBCURL_VERSION_NUM >= 0x072a00 /* 7.42.0 */
|
|
Packit Service |
97d2fb |
curl_easy_setopt(data[i].handle, CURLOPT_PATH_AS_IS, (long) 1);
|
|
Packit Service |
97d2fb |
#else
|
|
Packit Service |
97d2fb |
/* On old curl; no big deal, canonicalization here is almost the
|
|
Packit Service |
97d2fb |
same, except perhaps for ? # type decorations at the tail. */
|
|
Packit Service |
97d2fb |
#endif
|
|
Packit Service |
97d2fb |
curl_easy_setopt(data[i].handle, CURLOPT_AUTOREFERER, (long) 1);
|
|
Packit Service |
97d2fb |
curl_easy_setopt(data[i].handle, CURLOPT_ACCEPT_ENCODING, "");
|
|
Packit Service |
97d2fb |
curl_easy_setopt(data[i].handle, CURLOPT_HTTPHEADER, c->headers);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
curl_multi_add_handle(curlm, data[i].handle);
|
|
Packit Service |
97d2fb |
server_url = strtok_r(NULL, url_delim, &strtok_saveptr);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Query servers in parallel. */
|
|
Packit Service |
97d2fb |
int still_running;
|
|
Packit Service |
97d2fb |
long loops = 0;
|
|
Packit Service |
97d2fb |
do
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Wait 1 second, the minimum DEBUGINFOD_TIMEOUT. */
|
|
Packit Service |
97d2fb |
curl_multi_wait(curlm, NULL, 0, 1000, NULL);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* If the target file has been found, abort the other queries. */
|
|
Packit Service |
97d2fb |
if (target_handle != NULL)
|
|
Packit Service |
97d2fb |
for (int i = 0; i < num_urls; i++)
|
|
Packit Service |
97d2fb |
if (data[i].handle != target_handle)
|
|
Packit Service |
97d2fb |
curl_multi_remove_handle(curlm, data[i].handle);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
CURLMcode curlm_res = curl_multi_perform(curlm, &still_running);
|
|
Packit Service |
97d2fb |
if (curlm_res != CURLM_OK)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
switch (curlm_res)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
case CURLM_CALL_MULTI_PERFORM: continue;
|
|
Packit Service |
97d2fb |
case CURLM_OUT_OF_MEMORY: rc = -ENOMEM; break;
|
|
Packit Service |
97d2fb |
default: rc = -ENETUNREACH; break;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
goto out1;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (c->progressfn) /* inform/check progress callback */
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
loops ++;
|
|
Packit Service |
97d2fb |
long pa = loops; /* default params for progress callback */
|
|
Packit Service |
97d2fb |
long pb = 0; /* transfer_timeout tempting, but loops != elapsed-time */
|
|
Packit Service |
97d2fb |
if (target_handle) /* we've committed to a server; report its download progress */
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
CURLcode curl_res;
|
|
Packit Service |
97d2fb |
#ifdef CURLINFO_SIZE_DOWNLOAD_T
|
|
Packit Service |
97d2fb |
curl_off_t dl;
|
|
Packit Service |
97d2fb |
curl_res = curl_easy_getinfo(target_handle,
|
|
Packit Service |
97d2fb |
CURLINFO_SIZE_DOWNLOAD_T,
|
|
Packit Service |
97d2fb |
&dl);
|
|
Packit Service |
97d2fb |
if (curl_res == 0 && dl >= 0)
|
|
Packit Service |
97d2fb |
pa = (dl > LONG_MAX ? LONG_MAX : (long)dl);
|
|
Packit Service |
97d2fb |
#else
|
|
Packit Service |
97d2fb |
double dl;
|
|
Packit Service |
97d2fb |
curl_res = curl_easy_getinfo(target_handle,
|
|
Packit Service |
97d2fb |
CURLINFO_SIZE_DOWNLOAD,
|
|
Packit Service |
97d2fb |
&dl);
|
|
Packit Service |
97d2fb |
if (curl_res == 0)
|
|
Packit Service |
97d2fb |
pa = (dl > LONG_MAX ? LONG_MAX : (long)dl);
|
|
Packit Service |
97d2fb |
#endif
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* NB: If going through deflate-compressing proxies, this
|
|
Packit Service |
97d2fb |
number is likely to be unavailable, so -1 may show. */
|
|
Packit Service |
97d2fb |
#ifdef CURLINFO_CURLINFO_CONTENT_LENGTH_DOWNLOAD_T
|
|
Packit Service |
97d2fb |
curl_off_t cl;
|
|
Packit Service |
97d2fb |
curl_res = curl_easy_getinfo(target_handle,
|
|
Packit Service |
97d2fb |
CURLINFO_CONTENT_LENGTH_DOWNLOAD_T,
|
|
Packit Service |
97d2fb |
&cl);
|
|
Packit Service |
97d2fb |
if (curl_res == 0 && cl >= 0)
|
|
Packit Service |
97d2fb |
pb = (cl > LONG_MAX ? LONG_MAX : (long)cl);
|
|
Packit Service |
97d2fb |
#else
|
|
Packit Service |
97d2fb |
double cl;
|
|
Packit Service |
97d2fb |
curl_res = curl_easy_getinfo(target_handle,
|
|
Packit Service |
97d2fb |
CURLINFO_CONTENT_LENGTH_DOWNLOAD,
|
|
Packit Service |
97d2fb |
&cl);
|
|
Packit Service |
97d2fb |
if (curl_res == 0)
|
|
Packit Service |
97d2fb |
pb = (cl > LONG_MAX ? LONG_MAX : (long)cl);
|
|
Packit Service |
97d2fb |
#endif
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if ((*c->progressfn) (c, pa, pb))
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
} while (still_running);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Check whether a query was successful. If so, assign its handle
|
|
Packit Service |
97d2fb |
to verified_handle. */
|
|
Packit Service |
97d2fb |
int num_msg;
|
|
Packit Service |
97d2fb |
rc = -ENOENT;
|
|
Packit Service |
97d2fb |
CURL *verified_handle = NULL;
|
|
Packit Service |
97d2fb |
do
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
CURLMsg *msg;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
msg = curl_multi_info_read(curlm, &num_msg);
|
|
Packit Service |
97d2fb |
if (msg != NULL && msg->msg == CURLMSG_DONE)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (msg->data.result != CURLE_OK)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Unsucessful query, determine error code. */
|
|
Packit Service |
97d2fb |
switch (msg->data.result)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
case CURLE_COULDNT_RESOLVE_HOST: rc = -EHOSTUNREACH; break; // no NXDOMAIN
|
|
Packit Service |
97d2fb |
case CURLE_URL_MALFORMAT: rc = -EINVAL; break;
|
|
Packit Service |
97d2fb |
case CURLE_COULDNT_CONNECT: rc = -ECONNREFUSED; break;
|
|
Packit Service |
97d2fb |
case CURLE_PEER_FAILED_VERIFICATION: rc = -ECONNREFUSED; break;
|
|
Packit Service |
97d2fb |
case CURLE_REMOTE_ACCESS_DENIED: rc = -EACCES; break;
|
|
Packit Service |
97d2fb |
case CURLE_WRITE_ERROR: rc = -EIO; break;
|
|
Packit Service |
97d2fb |
case CURLE_OUT_OF_MEMORY: rc = -ENOMEM; break;
|
|
Packit Service |
97d2fb |
case CURLE_TOO_MANY_REDIRECTS: rc = -EMLINK; break;
|
|
Packit Service |
97d2fb |
case CURLE_SEND_ERROR: rc = -ECONNRESET; break;
|
|
Packit Service |
97d2fb |
case CURLE_RECV_ERROR: rc = -ECONNRESET; break;
|
|
Packit Service |
97d2fb |
case CURLE_OPERATION_TIMEDOUT: rc = -ETIME; break;
|
|
Packit Service |
97d2fb |
default: rc = -ENOENT; break;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Query completed without an error. Confirm that the
|
|
Packit Service |
97d2fb |
response code is 200 when using HTTP/HTTPS and 0 when
|
|
Packit Service |
97d2fb |
using file:// and set verified_handle. */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (msg->easy_handle != NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
char *effective_url = NULL;
|
|
Packit Service |
97d2fb |
long resp_code = 500;
|
|
Packit Service |
97d2fb |
CURLcode ok1 = curl_easy_getinfo (target_handle,
|
|
Packit Service |
97d2fb |
CURLINFO_EFFECTIVE_URL,
|
|
Packit Service |
97d2fb |
&effective_url);
|
|
Packit Service |
97d2fb |
CURLcode ok2 = curl_easy_getinfo (target_handle,
|
|
Packit Service |
97d2fb |
CURLINFO_RESPONSE_CODE,
|
|
Packit Service |
97d2fb |
&resp_code);
|
|
Packit Service |
97d2fb |
if(ok1 == CURLE_OK && ok2 == CURLE_OK && effective_url)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (strncasecmp (effective_url, "HTTP", 4) == 0)
|
|
Packit Service |
97d2fb |
if (resp_code == 200)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
verified_handle = msg->easy_handle;
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
if (strncasecmp (effective_url, "FILE", 4) == 0)
|
|
Packit Service |
97d2fb |
if (resp_code == 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
verified_handle = msg->easy_handle;
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
/* - libcurl since 7.52.0 version start to support
|
|
Packit Service |
97d2fb |
CURLINFO_SCHEME;
|
|
Packit Service |
97d2fb |
- before 7.61.0, effective_url would give us a
|
|
Packit Service |
97d2fb |
url with upper case SCHEME added in the front;
|
|
Packit Service |
97d2fb |
- effective_url between 7.61 and 7.69 can be lack
|
|
Packit Service |
97d2fb |
of scheme if the original url doesn't include one;
|
|
Packit Service |
97d2fb |
- since version 7.69 effective_url will be provide
|
|
Packit Service |
97d2fb |
a scheme in lower case. */
|
|
Packit Service |
97d2fb |
#if LIBCURL_VERSION_NUM >= 0x073d00 /* 7.61.0 */
|
|
Packit Service |
97d2fb |
#if LIBCURL_VERSION_NUM <= 0x074500 /* 7.69.0 */
|
|
Packit Service |
97d2fb |
char *scheme = NULL;
|
|
Packit Service |
97d2fb |
CURLcode ok3 = curl_easy_getinfo (target_handle,
|
|
Packit Service |
97d2fb |
CURLINFO_SCHEME,
|
|
Packit Service |
97d2fb |
&scheme);
|
|
Packit Service |
97d2fb |
if(ok3 == CURLE_OK && scheme)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (strncmp (scheme, "HTTP", 4) == 0)
|
|
Packit Service |
97d2fb |
if (resp_code == 200)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
verified_handle = msg->easy_handle;
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
#endif
|
|
Packit Service |
97d2fb |
#endif
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
} while (num_msg > 0);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (verified_handle == NULL)
|
|
Packit Service |
97d2fb |
goto out1;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* we've got one!!!! */
|
|
Packit Service |
97d2fb |
time_t mtime;
|
|
Packit Service |
97d2fb |
CURLcode curl_res = curl_easy_getinfo(verified_handle, CURLINFO_FILETIME, (void*) &mtime);
|
|
Packit Service |
97d2fb |
if (curl_res != CURLE_OK)
|
|
Packit Service |
97d2fb |
mtime = time(NULL); /* fall back to current time */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
struct timeval tvs[2];
|
|
Packit Service |
97d2fb |
tvs[0].tv_sec = tvs[1].tv_sec = mtime;
|
|
Packit Service |
97d2fb |
tvs[0].tv_usec = tvs[1].tv_usec = 0;
|
|
Packit Service |
97d2fb |
(void) futimes (fd, tvs); /* best effort */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* rename tmp->real */
|
|
Packit Service |
97d2fb |
rc = rename (target_cache_tmppath, target_cache_path);
|
|
Packit Service |
97d2fb |
if (rc < 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
rc = -errno;
|
|
Packit Service |
97d2fb |
goto out1;
|
|
Packit Service |
97d2fb |
/* Perhaps we need not give up right away; could retry or something ... */
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Success!!!! */
|
|
Packit Service |
97d2fb |
for (int i = 0; i < num_urls; i++)
|
|
Packit Service |
97d2fb |
curl_easy_cleanup(data[i].handle);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
curl_multi_cleanup (curlm);
|
|
Packit Service |
97d2fb |
free (data);
|
|
Packit Service |
97d2fb |
free (server_urls);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* don't close fd - we're returning it */
|
|
Packit Service |
97d2fb |
/* don't unlink the tmppath; it's already been renamed. */
|
|
Packit Service |
97d2fb |
if (path != NULL)
|
|
Packit Service |
97d2fb |
*path = strdup(target_cache_path);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
rc = fd;
|
|
Packit Service |
97d2fb |
goto out;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* error exits */
|
|
Packit Service |
97d2fb |
out1:
|
|
Packit Service |
97d2fb |
for (int i = 0; i < num_urls; i++)
|
|
Packit Service |
97d2fb |
curl_easy_cleanup(data[i].handle);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
curl_multi_cleanup(curlm);
|
|
Packit Service |
97d2fb |
unlink (target_cache_tmppath);
|
|
Packit Service |
97d2fb |
close (fd); /* before the rmdir, otherwise it'll fail */
|
|
Packit Service |
97d2fb |
(void) rmdir (target_cache_dir); /* nop if not empty */
|
|
Packit Service |
97d2fb |
free(data);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
out0:
|
|
Packit Service |
97d2fb |
free (server_urls);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* general purpose exit */
|
|
Packit Service |
97d2fb |
out:
|
|
Packit Service |
97d2fb |
/* Conclude the last \r status line */
|
|
Packit Service |
97d2fb |
/* Another possibility is to use the ANSI CSI n K EL "Erase in Line"
|
|
Packit Service |
97d2fb |
code. That way, the previously printed messages would be erased,
|
|
Packit Service |
97d2fb |
and without a newline. */
|
|
Packit Service |
97d2fb |
if (c->default_progressfn_printed_p)
|
|
Packit Service |
97d2fb |
dprintf(STDERR_FILENO, "\n");
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
free (cache_path);
|
|
Packit Service |
97d2fb |
free (maxage_path);
|
|
Packit Service |
97d2fb |
free (interval_path);
|
|
Packit Service |
97d2fb |
free (target_cache_dir);
|
|
Packit Service |
97d2fb |
free (target_cache_path);
|
|
Packit Service |
97d2fb |
free (target_cache_tmppath);
|
|
Packit Service |
97d2fb |
return rc;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* See debuginfod.h */
|
|
Packit Service |
97d2fb |
debuginfod_client *
|
|
Packit Service |
97d2fb |
debuginfod_begin (void)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
debuginfod_client *client;
|
|
Packit Service |
97d2fb |
size_t size = sizeof (struct debuginfod_client);
|
|
Packit Service |
97d2fb |
client = (debuginfod_client *) calloc (1, size);
|
|
Packit Service |
97d2fb |
if (client != NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (getenv(DEBUGINFOD_PROGRESS_ENV_VAR))
|
|
Packit Service |
97d2fb |
client->progressfn = default_progressfn;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
return client;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
void
|
|
Packit Service |
97d2fb |
debuginfod_set_user_data(debuginfod_client *client,
|
|
Packit Service |
97d2fb |
void *data)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
client->user_data = data;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
void *
|
|
Packit Service |
97d2fb |
debuginfod_get_user_data(debuginfod_client *client)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
return client->user_data;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
const char *
|
|
Packit Service |
97d2fb |
debuginfod_get_url(debuginfod_client *client)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
return client->url;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
void
|
|
Packit Service |
97d2fb |
debuginfod_end (debuginfod_client *client)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (client == NULL)
|
|
Packit Service |
97d2fb |
return;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
curl_slist_free_all (client->headers);
|
|
Packit Service |
97d2fb |
free (client->url);
|
|
Packit Service |
97d2fb |
free (client);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
int
|
|
Packit Service |
97d2fb |
debuginfod_find_debuginfo (debuginfod_client *client,
|
|
Packit Service |
97d2fb |
const unsigned char *build_id, int build_id_len,
|
|
Packit Service |
97d2fb |
char **path)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
return debuginfod_query_server(client, build_id, build_id_len,
|
|
Packit Service |
97d2fb |
"debuginfo", NULL, path);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* See debuginfod.h */
|
|
Packit Service |
97d2fb |
int
|
|
Packit Service |
97d2fb |
debuginfod_find_executable(debuginfod_client *client,
|
|
Packit Service |
97d2fb |
const unsigned char *build_id, int build_id_len,
|
|
Packit Service |
97d2fb |
char **path)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
return debuginfod_query_server(client, build_id, build_id_len,
|
|
Packit Service |
97d2fb |
"executable", NULL, path);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* See debuginfod.h */
|
|
Packit Service |
97d2fb |
int debuginfod_find_source(debuginfod_client *client,
|
|
Packit Service |
97d2fb |
const unsigned char *build_id, int build_id_len,
|
|
Packit Service |
97d2fb |
const char *filename, char **path)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
return debuginfod_query_server(client, build_id, build_id_len,
|
|
Packit Service |
97d2fb |
"source", filename, path);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Add an outgoing HTTP header. */
|
|
Packit Service |
97d2fb |
int debuginfod_add_http_header (debuginfod_client *client, const char* header)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Sanity check header value is of the form Header: Value.
|
|
Packit Service |
97d2fb |
It should contain exactly one colon that isn't the first or
|
|
Packit Service |
97d2fb |
last character. */
|
|
Packit Service |
97d2fb |
char *colon = strchr (header, ':');
|
|
Packit Service |
97d2fb |
if (colon == NULL
|
|
Packit Service |
97d2fb |
|| colon == header
|
|
Packit Service |
97d2fb |
|| *(colon + 1) == '\0'
|
|
Packit Service |
97d2fb |
|| strchr (colon + 1, ':') != NULL)
|
|
Packit Service |
97d2fb |
return -EINVAL;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
struct curl_slist *temp = curl_slist_append (client->headers, header);
|
|
Packit Service |
97d2fb |
if (temp == NULL)
|
|
Packit Service |
97d2fb |
return -ENOMEM;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Track if User-Agent: is being set. If so, signal not to add the
|
|
Packit Service |
97d2fb |
default one. */
|
|
Packit Service |
97d2fb |
if (strncmp (header, "User-Agent:", 11) == 0)
|
|
Packit Service |
97d2fb |
client->user_agent_set_p = 1;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
client->headers = temp;
|
|
Packit Service |
97d2fb |
return 0;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
void
|
|
Packit Service |
97d2fb |
debuginfod_set_progressfn(debuginfod_client *client,
|
|
Packit Service |
97d2fb |
debuginfod_progressfn_t fn)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
client->progressfn = fn;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* NB: these are thread-unsafe. */
|
|
Packit Service |
97d2fb |
__attribute__((constructor)) attribute_hidden void libdebuginfod_ctor(void)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
curl_global_init(CURL_GLOBAL_DEFAULT);
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* NB: this is very thread-unsafe: it breaks other threads that are still in libcurl */
|
|
Packit Service |
97d2fb |
__attribute__((destructor)) attribute_hidden void libdebuginfod_dtor(void)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* ... so don't do this: */
|
|
Packit Service |
97d2fb |
/* curl_global_cleanup(); */
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
#endif /* DUMMY_LIBDEBUGINFOD */
|