#include "defs.h"
 #include <errno.h>
-#include "cli/cli-style.h"
 #include "gdbsupport/scoped_fd.h"
 #include "debuginfod-support.h"
 #include "gdbsupport/gdb_optional.h"
+#include "cli/cli-cmds.h"
+#include "cli/cli-style.h"
+
+/* Set/show debuginfod commands.  */
+static cmd_list_element *set_debuginfod_prefix_list;
+static cmd_list_element *show_debuginfod_prefix_list;
+
+static const char debuginfod_on[] = "on";
+static const char debuginfod_off[] = "off";
+static const char debuginfod_ask[] = "ask";
+
+static const char *debuginfod_enable = debuginfod_ask;
+static unsigned debuginfod_verbose = 1;
 
 #ifndef HAVE_LIBDEBUGINFOD
 scoped_fd
 {
   return scoped_fd (-ENOSYS);
 }
+
+#define NO_IMPL _("Support for debuginfod is not compiled into GDB.")
+
+/* Stub set/show commands that indicate debuginfod is not supported.  */
+
+static void
+set_debuginfod_on_command (const char *args, int from_tty)
+{
+  error (NO_IMPL);
+  debuginfod_enable = debuginfod_off;
+}
+
+static void
+set_debuginfod_off_command (const char *args, int from_tty)
+{
+  error (NO_IMPL);
+  debuginfod_enable = debuginfod_off;
+}
+
+static void
+set_debuginfod_ask_command (const char *args, int from_tty)
+{
+  error (NO_IMPL);
+  debuginfod_enable = debuginfod_off;
+}
+
+static void
+show_debuginfod_status_command (const char *args, int from_tty)
+{
+  error (NO_IMPL);
+}
+
+static void
+set_debuginfod_urls_command (const std::string& urls)
+{
+  error (NO_IMPL);
+}
+
+static const std::string&
+get_debuginfod_urls_command ()
+{
+  static std::string empty;
+  return empty;
+}
+
+static void
+show_debuginfod_urls_command (struct ui_file *file, int from_tty,
+                             struct cmd_list_element *cmd, const char *value)
+{
+  error (NO_IMPL);
+}
+
+static void
+set_debuginfod_verbose_command (const char *args, int from_tty,
+                               struct cmd_list_element *c)
+{
+  error (NO_IMPL);
+  debuginfod_verbose = 0;
+}
+
+static void
+show_debuginfod_verbose_command (struct ui_file *file, int from_tty,
+                                struct cmd_list_element *cmd,
+                                const char *value)
+{
+  error (NO_IMPL);
+}
 #else
 #include <elfutils/debuginfod.h>
 
 using debuginfod_client_up
   = std::unique_ptr<debuginfod_client, debuginfod_client_deleter>;
 
+/* Enable debuginfod.  */
+
+static void
+set_debuginfod_on_command (const char *args, int from_tty)
+{
+  debuginfod_enable = debuginfod_on;
+}
+
+/* Disable debuginfod.  */
+
+static void
+set_debuginfod_off_command (const char *args, int from_tty)
+{
+  debuginfod_enable = debuginfod_off;
+}
+
+/* Before next query, ask user whether to enable debuginfod.  */
+
+static void
+set_debuginfod_ask_command (const char *args, int from_tty)
+{
+  debuginfod_enable = debuginfod_ask;
+}
+
+/* Show whether debuginfod is enabled.  */
+
+static void
+show_debuginfod_status_command (const char *args, int from_tty)
+{
+  printf_unfiltered (_("Debuginfod functionality is currently set to " \
+                    "\"%s\".\n"), debuginfod_enable);
+}
+
+/* Set the URLs that debuginfod will query.  */
+
+static void
+set_debuginfod_urls_command (const std::string& urls)
+{
+  if (setenv ("DEBUGINFOD_URLS", urls.c_str (), 1) != 0)
+    warning (_("Unable to set debuginfod URLs: %s"), safe_strerror (errno));
+}
+
+/* Get current debuginfod URLs.  */
+
+static const std::string&
+get_debuginfod_urls_command ()
+{
+  static std::string urls;
+  const char *envvar = getenv (DEBUGINFOD_URLS_ENV_VAR);
+
+  if (envvar != nullptr)
+    urls = envvar;
+  else
+    urls.clear ();
+
+  return urls;
+}
+
+/* Show the URLs that debuginfod will query.  */
+
+static void
+show_debuginfod_urls_command (struct ui_file *file, int from_tty,
+                             struct cmd_list_element *cmd, const char *value)
+{
+  if (value == nullptr || value[0] == '\0')
+    fprintf_unfiltered (file, _("Debuginfod URLs have not been set.\n"));
+  else
+    fprintf_filtered (file, _("Debuginfod URLs are currently set to:\n%s\n"),
+                     value);
+}
+
+/* No-op setter used for compatibility when gdb is built without debuginfod.  */
+
+static void
+set_debuginfod_verbose_command (const char *args, int from_tty,
+                               struct cmd_list_element *c)
+{
+  return;
+}
+
+/* Show verbosity.  */
+
+static void
+show_debuginfod_verbose_command (struct ui_file *file, int from_tty,
+                                struct cmd_list_element *cmd, const char *value)
+{
+  fprintf_filtered (file, _("Debuginfod verbose output is set to %s.\n"),
+                   value);
+}
+
 static int
 progressfn (debuginfod_client *c, long cur, long total)
 {
   return global_client.get ();
 }
 
+/* Check if debuginfod is enabled.  If configured to do so, ask the user
+   whether to enable debuginfod.  */
+
+static bool
+debuginfod_enabled ()
+{
+  const char *urls = getenv (DEBUGINFOD_URLS_ENV_VAR);
+
+  if (urls == nullptr || urls[0] == '\0'
+      || debuginfod_enable == debuginfod_off)
+    return false;
+
+  if (debuginfod_enable == debuginfod_ask)
+    {
+      int resp = nquery (_("\nThis GDB supports auto-downloading debuginfo " \
+                          "from the following URLs:\n%s\nEnable debuginfod " \
+                          "for this session? "),
+                        urls);
+      if (!resp)
+       {
+         printf_filtered (_("Debuginfod has been disabled.\nTo make this " \
+                            "setting permanent, add \'set debuginfod off\' " \
+                            "to .gdbinit.\n"));
+         debuginfod_enable = debuginfod_off;
+         return false;
+       }
+
+      printf_filtered (_("Debuginfod has been enabled.\nTo make this " \
+                        "setting permanent, add \'set debuginfod on\' " \
+                        "to .gdbinit.\n"));
+      debuginfod_enable = debuginfod_on;
+    }
+
+  return true;
+}
+
 /* See debuginfod-support.h  */
 
 scoped_fd
                         const char *srcpath,
                         gdb::unique_xmalloc_ptr<char> *destname)
 {
-  const char *urls_env_var = getenv (DEBUGINFOD_URLS_ENV_VAR);
-  if (urls_env_var == NULL || urls_env_var[0] == '\0')
+  if (!debuginfod_enabled ())
     return scoped_fd (-ENOSYS);
 
   debuginfod_client *c = get_debuginfod_client ();
                                        nullptr));
   debuginfod_set_user_data (c, nullptr);
 
-  /* TODO: Add 'set debug debuginfod' command to control when error messages are shown.  */
-  if (fd.get () < 0 && fd.get () != -ENOENT)
+  if (debuginfod_verbose > 0 && fd.get () < 0 && fd.get () != -ENOENT)
     printf_filtered (_("Download failed: %s.  Continuing without source file %ps.\n"),
                     safe_strerror (-fd.get ()),
                     styled_string (file_name_style.style (),  srcpath));
                            const char *filename,
                            gdb::unique_xmalloc_ptr<char> *destname)
 {
-  const char *urls_env_var = getenv (DEBUGINFOD_URLS_ENV_VAR);
-  if (urls_env_var == NULL || urls_env_var[0] == '\0')
+  if (!debuginfod_enabled ())
     return scoped_fd (-ENOSYS);
 
   debuginfod_client *c = get_debuginfod_client ();
                                           &dname));
   debuginfod_set_user_data (c, nullptr);
 
-  if (fd.get () < 0 && fd.get () != -ENOENT)
+  if (debuginfod_verbose > 0 && fd.get () < 0 && fd.get () != -ENOENT)
     printf_filtered (_("Download failed: %s.  Continuing without debug info for %ps.\n"),
                     safe_strerror (-fd.get ()),
                     styled_string (file_name_style.style (),  filename));
   return fd;
 }
 #endif
+
+/* Register debuginfod commands.  */
+
+void _initialize_debuginfod ();
+void
+_initialize_debuginfod ()
+{
+  /* set/show debuginfod */
+  add_setshow_prefix_cmd ("debuginfod", class_run,
+                         _("Set debuginfod options"),
+                         _("Show debuginfod options"),
+                         &set_debuginfod_prefix_list,
+                         &show_debuginfod_prefix_list,
+                         &setlist, &showlist);
+
+  /* set debuginfod on */
+  add_cmd ("on", class_run, set_debuginfod_on_command,
+          _("Enable debuginfod."), &set_debuginfod_prefix_list);
+
+  /* set debuginfod off */
+  add_cmd ("off", class_run, set_debuginfod_off_command,
+          _("Disable debuginfod."), &set_debuginfod_prefix_list);
+
+  /* set debuginfod ask */
+  add_cmd ("ask", class_run, set_debuginfod_ask_command, _("\
+Ask the user whether to enable debuginfod before performing the next query."),
+          &set_debuginfod_prefix_list);
+
+  /* show debuginfod status */
+  add_cmd ("status", class_run, show_debuginfod_status_command,
+          _("Show whether debuginfod is set to \"on\", \"off\" or \"ask\"."),
+          &show_debuginfod_prefix_list);
+
+  /* set/show debuginfod urls */
+  add_setshow_string_noescape_cmd ("urls", class_run, _("\
+Set the list of debuginfod server URLs."), _("\
+Show the list of debuginfod server URLs."), _("\
+Manage the space-separated list of debuginfod server URLs that GDB will query \
+when missing debuginfo, executables or source files.\nThe default value is \
+copied from the DEBUGINFOD_URLS environment variable."),
+                                  set_debuginfod_urls_command,
+                                  get_debuginfod_urls_command,
+                                  show_debuginfod_urls_command,
+                                  &set_debuginfod_prefix_list,
+                                  &show_debuginfod_prefix_list);
+
+  /* set/show debuginfod verbose */
+  add_setshow_zuinteger_cmd ("verbose", class_support,
+                            &debuginfod_verbose, _("\
+Set verbosity of debuginfod output."), _("\
+Show debuginfod debugging."), _("\
+When set to a non-zero value, display verbose output for each debuginfod \
+query.\nTo disable, set to zero.  Verbose output is displayed by default."),
+                            set_debuginfod_verbose_command,
+                            show_debuginfod_verbose_command,
+                            &set_debuginfod_prefix_list,
+                            &show_debuginfod_prefix_list);
+}