Blame debuginfod/debuginfod-find.c

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