Blame debuginfod/debuginfod-client.c

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