Blame debuginfod/debuginfod-find.c

Packit 032894
/* Command-line frontend for retrieving ELF / DWARF / source files
Packit 032894
   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 the GNU General Public License as published by
Packit 032894
   the Free Software Foundation; either version 3 of the License, or
Packit 032894
   (at your option) any later version.
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
#include "config.h"
Packit 032894
#include "printversion.h"
Packit 032894
#include "debuginfod.h"
Packit 032894
#include <errno.h>
Packit 032894
#include <stdio.h>
Packit 032894
#include <stdlib.h>
Packit 032894
#include <string.h>
Packit 032894
#include <argp.h>
Packit Service 35cfd5
#include <unistd.h>
Packit Service 35cfd5
#include <fcntl.h>
Packit Service 35cfd5
#include <gelf.h>
Packit Service 35cfd5
#include <libdwelf.h>
Packit 032894
Packit 032894
Packit 032894
/* Name and version of program.  */
Packit 032894
ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
Packit 032894
Packit 032894
/* Bug report address.  */
Packit 032894
ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
Packit 032894
Packit 032894
/* Short description of program.  */
Packit 032894
static const char doc[] = N_("Request debuginfo-related content "
Packit 032894
                             "from debuginfods listed in $" DEBUGINFOD_URLS_ENV_VAR ".");
Packit 032894
Packit 032894
/* Strings for arguments in help texts.  */
Packit 032894
static const char args_doc[] = N_("debuginfo BUILDID\n"
Packit Service 35cfd5
                                  "debuginfo PATH\n"
Packit 032894
                                  "executable BUILDID\n"
Packit Service 35cfd5
                                  "executable PATH\n"
Packit Service 35cfd5
                                  "source BUILDID /FILENAME\n"
Packit Service 35cfd5
                                  "source PATH /FILENAME\n");
Packit Service 35cfd5
Packit 032894
Packit 032894
/* Definitions of arguments for argp functions.  */
Packit 032894
static const struct argp_option options[] =
Packit 032894
  {
Packit 032894
   { "verbose", 'v', NULL, 0, "Increase verbosity.", 0 },
Packit 032894
   { NULL, 0, NULL, 0, NULL, 0 }
Packit 032894
  };
Packit 032894
Packit 032894
/* debuginfod connection handle.  */
Packit 032894
static debuginfod_client *client;
Packit Service 35cfd5
static int verbose;
Packit 032894
Packit 032894
int progressfn(debuginfod_client *c __attribute__((__unused__)),
Packit 032894
	       long a, long b)
Packit 032894
{
Packit 032894
  fprintf (stderr, "Progress %ld / %ld\n", a, b);
Packit 032894
  return 0;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
static error_t parse_opt (int key, char *arg, struct argp_state *state)
Packit 032894
{
Packit 032894
  (void) arg;
Packit 032894
  (void) state;
Packit 032894
  switch (key)
Packit 032894
    {
Packit Service 35cfd5
    case 'v': verbose++;
Packit Service 35cfd5
      debuginfod_set_progressfn (client, & progressfn); break;
Packit 032894
    default: return ARGP_ERR_UNKNOWN;
Packit 032894
    }
Packit 032894
  return 0;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
/* Data structure to communicate with argp functions.  */
Packit 032894
static struct argp argp =
Packit 032894
  {
Packit 032894
   options, parse_opt, args_doc, doc, NULL, NULL, NULL
Packit 032894
  };
Packit 032894
Packit 032894
Packit 032894
Packit 032894
int
Packit 032894
main(int argc, char** argv)
Packit 032894
{
Packit Service 35cfd5
  elf_version (EV_CURRENT);
Packit Service 35cfd5
Packit 032894
  client = debuginfod_begin ();
Packit 032894
  if (client == NULL)
Packit 032894
    {
Packit 032894
      fprintf(stderr, "Couldn't create debuginfod client context\n");
Packit 032894
      return 1;
Packit 032894
    }
Packit 032894
Packit Service 35cfd5
  /* Exercise user data pointer, to support testing only. */
Packit Service 35cfd5
  debuginfod_set_user_data (client, (void *)"Progress");
Packit Service 35cfd5
Packit 032894
  int remaining;
Packit 032894
  (void) argp_parse (&argp, argc, argv, ARGP_IN_ORDER|ARGP_NO_ARGS, &remaining, NULL);
Packit 032894
Packit 032894
  if (argc < 2 || remaining+1 == argc) /* no arguments or at least two non-option words */
Packit 032894
    {
Packit 032894
      argp_help (&argp, stderr, ARGP_HELP_USAGE, argv[0]);
Packit 032894
      return 1;
Packit 032894
    }
Packit 032894
Packit Service 35cfd5
  /* If we were passed an ELF file name in the BUILDID slot, look in there. */
Packit Service 35cfd5
  unsigned char* build_id = (unsigned char*) argv[remaining+1];
Packit Service 35cfd5
  int build_id_len = 0; /* assume text */
Packit Service 35cfd5
Packit Service 35cfd5
  int any_non_hex = 0;
Packit Service 35cfd5
  int i;
Packit Service 35cfd5
  for (i = 0; build_id[i] != '\0'; i++)
Packit Service 35cfd5
    if ((build_id[i] >= '0' && build_id[i] <= '9') ||
Packit Service 35cfd5
        (build_id[i] >= 'a' && build_id[i] <= 'f'))
Packit Service 35cfd5
      ;
Packit Service 35cfd5
    else
Packit Service 35cfd5
      any_non_hex = 1;
Packit Service 35cfd5
Packit Service 35cfd5
  int fd = -1;
Packit Service 35cfd5
  Elf* elf = NULL;
Packit Service 35cfd5
  if (any_non_hex) /* raw build-id */
Packit Service 35cfd5
    {
Packit Service 35cfd5
      fd = open ((char*) build_id, O_RDONLY);
Packit Service 35cfd5
      if (fd < 0)
Packit Service 35cfd5
        fprintf (stderr, "Cannot open %s: %s\n", build_id, strerror(errno));
Packit Service 35cfd5
    }
Packit Service 35cfd5
  if (fd >= 0)
Packit Service 35cfd5
    {
Packit Service 35cfd5
      elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
Packit Service 35cfd5
      if (elf == NULL)
Packit Service 35cfd5
        fprintf (stderr, "Cannot elf_begin %s: %s\n", build_id, elf_errmsg(-1));
Packit Service 35cfd5
    }
Packit Service 35cfd5
  if (elf != NULL)
Packit Service 35cfd5
    {
Packit Service 35cfd5
      const void *extracted_build_id;
Packit Service 35cfd5
      ssize_t s = dwelf_elf_gnu_build_id(elf, &extracted_build_id);
Packit Service 35cfd5
      if (s > 0)
Packit Service 35cfd5
        {
Packit Service 35cfd5
          /* Success: replace the build_id pointer/len with the binary blob
Packit Service 35cfd5
             that elfutils is keeping for us.  It'll remain valid until elf_end(). */
Packit Service 35cfd5
          build_id = (unsigned char*) extracted_build_id;
Packit Service 35cfd5
          build_id_len = s;
Packit Service 35cfd5
        }
Packit Service 35cfd5
      else
Packit Service 35cfd5
        fprintf (stderr, "Cannot extract build-id from %s: %s\n", build_id, elf_errmsg(-1));
Packit Service 35cfd5
    }
Packit Service 35cfd5
Packit 032894
  char *cache_name;
Packit Service 35cfd5
  int rc = 0;
Packit 032894
Packit 032894
  /* Check whether FILETYPE is valid and call the appropriate
Packit 032894
     debuginfod_find_* function. If FILETYPE is "source"
Packit 032894
     then ensure a FILENAME was also supplied as an argument.  */
Packit 032894
  if (strcmp(argv[remaining], "debuginfo") == 0)
Packit 032894
    rc = debuginfod_find_debuginfo(client,
Packit Service 35cfd5
				   build_id, build_id_len,
Packit 032894
				   &cache_name);
Packit 032894
  else if (strcmp(argv[remaining], "executable") == 0)
Packit 032894
    rc = debuginfod_find_executable(client,
Packit Service 35cfd5
                                    build_id, build_id_len,
Packit 032894
				    &cache_name);
Packit 032894
  else if (strcmp(argv[remaining], "source") == 0)
Packit 032894
    {
Packit Service 35cfd5
      if (remaining+2 == argc || argv[remaining+2][0] != '/')
Packit 032894
        {
Packit 032894
          fprintf(stderr, "If FILETYPE is \"source\" then absolute /FILENAME must be given\n");
Packit 032894
          return 1;
Packit 032894
        }
Packit Service 35cfd5
      rc = debuginfod_find_source(client,
Packit Service 35cfd5
                                  build_id, build_id_len,
Packit Service 35cfd5
				  argv[remaining+2], &cache_name);
Packit 032894
    }
Packit 032894
  else
Packit 032894
    {
Packit 032894
      argp_help (&argp, stderr, ARGP_HELP_USAGE, argv[0]);
Packit 032894
      return 1;
Packit 032894
    }
Packit 032894
Packit Service 35cfd5
  if (verbose)
Packit Service 35cfd5
    {
Packit Service 35cfd5
      const char* url = debuginfod_get_url (client);
Packit Service 35cfd5
      if (url != NULL)
Packit Service 35cfd5
        fprintf(stderr, "Downloaded from %s\n", url);
Packit Service 35cfd5
    }
Packit Service 35cfd5
Packit Service 35cfd5
  debuginfod_end (client);
Packit Service 35cfd5
  if (elf)
Packit Service 35cfd5
    elf_end(elf);
Packit Service 35cfd5
  if (fd >= 0)
Packit Service 35cfd5
    close (fd);
Packit Service 35cfd5
Packit 032894
  if (rc < 0)
Packit 032894
    {
Packit 032894
      fprintf(stderr, "Server query failed: %s\n", strerror(-rc));
Packit 032894
      return 1;
Packit 032894
    }
Packit 032894
Packit 032894
  printf("%s\n", cache_name);
Packit 032894
  free (cache_name);
Packit 032894
Packit 032894
  return 0;
Packit 032894
}