Blame debuginfod/debuginfod-client.c

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 */