crash: Convert list_proxies to chromeos-dbus-bindings generator

Switch list_proxies to using chromeos-dbus-bindings generator
in order to remove the glib dependency.

BUG=brillo:89
TEST=`FEATURES=test emerge-panther crash-reporter`
TEST=manually tested `list_proxies --quiet      \
       https://clieents2.google.com/cr/report`

Change-Id: Ic52277b2e14514376f4d55e627e1651d9ef566c5
Reviewed-on: https://chromium-review.googlesource.com/248781
Reviewed-by: Dan Erat <derat@chromium.org>
Trybot-Ready: Dan Erat <derat@chromium.org>
Commit-Queue: Steve Fung <stevefung@chromium.org>
Tested-by: Steve Fung <stevefung@chromium.org>
diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp
index 6e1ceac..988cb5d 100644
--- a/crash_reporter/crash-reporter.gyp
+++ b/crash_reporter/crash-reporter.gyp
@@ -6,7 +6,6 @@
     'variables': {
       'deps': [
         'libchromeos-<(libbase_ver)',
-        'dbus-glib-1',
       ],
     },
   },
@@ -16,8 +15,6 @@
       'type': 'static_library',
       'variables': {
         'exported_deps': [
-          'glib-2.0',
-          'gobject-2.0',
           'libchrome-<(libbase_ver)',
           'libpcrecpp',
         ],
@@ -68,7 +65,6 @@
       'variables': {
         'deps': [
           'dbus-1',
-          'dbus-glib-1',
           'libmetrics-<(libbase_ver)',
         ],
       },
@@ -85,13 +81,24 @@
       'variables': {
         'deps': [
           'dbus-1',
-          'dbus-glib-1',
           'libchrome-<(libbase_ver)',
         ],
       },
       'sources': [
         'list_proxies.cc',
       ],
+      'actions': [
+        {
+          'action_name': 'generate-lib-cros-service-proxies',
+          'variables': {
+            'proxy_output_file': 'include/libcrosservice/dbus-proxies.h'
+          },
+          'sources': [
+            './dbus_bindings/org.chromium.LibCrosService.xml',
+          ],
+          'includes': ['../common-mk/generate-dbus-proxies.gypi'],
+        },
+      ],
     },
     {
       'target_name': 'warn_collector',
diff --git a/crash_reporter/dbus_bindings/org.chromium.LibCrosService.xml b/crash_reporter/dbus_bindings/org.chromium.LibCrosService.xml
new file mode 100644
index 0000000..64b8b84
--- /dev/null
+++ b/crash_reporter/dbus_bindings/org.chromium.LibCrosService.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/org/chromium/LibCrosService"
+      xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+  <interface name="org.chromium.LibCrosServiceInterface">
+    <method name="ResolveNetworkProxy">
+      <arg name="source_url" type="s" direction="in"/>
+      <arg name="signal_interface" type="s" direction="in"/>
+      <arg name="signal_name" type="s" direction="in"/>
+      <annotation name="org.chromium.DBus.Method.Kind" value="simple"/>
+    </method>
+  </interface>
+  <interface name="org.chromium.CrashReporterLibcrosProxyResolvedInterface">
+    <signal name="ProxyResolved">
+      <arg name="source_url" type="s" direction="out"/>
+      <arg name="proxy_info" type="s" direction="out"/>
+      <arg name="error_message" type="s" direction="out"/>
+    </signal>
+  </interface>
+</node>
diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc
index 6cac8f8..de02f0a 100644
--- a/crash_reporter/list_proxies.cc
+++ b/crash_reporter/list_proxies.cc
@@ -2,35 +2,36 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <dbus/dbus-glib-lowlevel.h>
-#include <glib.h>
+#include <sysexits.h>
 #include <unistd.h>  // for isatty()
 
-#include <deque>
 #include <string>
+#include <vector>
 
+#include <base/cancelable_callback.h>
 #include <base/command_line.h>
 #include <base/files/file_util.h>
+#include <base/memory/weak_ptr.h>
 #include <base/strings/string_number_conversions.h>
 #include <base/strings/string_tokenizer.h>
 #include <base/strings/string_util.h>
 #include <base/values.h>
-#include <chromeos/dbus/dbus.h>
+#include <chromeos/daemons/dbus_daemon.h>
 #include <chromeos/syslog_logging.h>
 
-const char kLibCrosProxyResolveSignalInterface[] =
+#include "libcrosservice/dbus-proxies.h"
+
+using std::unique_ptr;
+
+namespace {
+
+const char kLibCrosProxyResolvedSignalInterface[] =
     "org.chromium.CrashReporterLibcrosProxyResolvedInterface";
-const char kLibCrosProxyResolveName[] = "ProxyResolved";
-const char kLibCrosServiceInterface[] = "org.chromium.LibCrosServiceInterface";
+const char kLibCrosProxyResolvedName[] = "ProxyResolved";
 const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
-const char kLibCrosServicePath[] = "/org/chromium/LibCrosService";
-const char kLibCrosServiceResolveNetworkProxyMethodName[] =
-    "ResolveNetworkProxy";
 const char kNoProxy[] = "direct://";
 
-namespace switches {
-
-const unsigned kTimeoutDefault = 5;
+const int kTimeoutDefaultSeconds = 5;
 
 const char kHelp[] = "help";
 const char kQuiet[] = "quiet";
@@ -46,19 +47,11 @@
     "  --timeout=N  Set timeout for browser resolving proxies (default is 5)\n"
     "  --help       Show this help.\n";
 
-}  // namespace switches
-
-static const char *GetGErrorMessage(const GError *error) {
-  if (!error)
-    return "Unknown error.";
-  return error->message;
-}
-
 // Copied from src/update_engine/chrome_browser_proxy_resolver.cc
 // Parses the browser's answer for resolved proxies.  It returns a
 // list of strings, each of which is a resolved proxy.
-std::deque<std::string> ParseProxyString(const std::string &input) {
-  std::deque<std::string> ret;
+std::vector<std::string> ParseProxyString(const std::string& input) {
+  std::vector<std::string> ret;
   // Some of this code taken from
   // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and
   // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc
@@ -99,130 +92,173 @@
   return ret;
 }
 
-// Define a signal-watcher class to handle the D-Bus signal sent to us when
-// the browser answers our request to resolve proxies.
-class BrowserProxyResolvedSignalWatcher : public chromeos::dbus::SignalWatcher {
+// A class for interfacing with Chrome to resolve proxies for a given source
+// url.  The class is initialized with the given source url to check, the
+// signal interface and name that Chrome will reply to, and how long to wait
+// for the resolve request to timeout.  Once initialized, the Run() function
+// must be called, which blocks on the D-Bus call to Chrome.  The call returns
+// after either the timeout or the proxy has been resolved.  The resolved
+// proxies can then be accessed through the proxies() function.
+class ProxyResolver : public chromeos::DBusDaemon {
  public:
-  explicit BrowserProxyResolvedSignalWatcher(GMainLoop *main_loop,
-                                             std::deque<std::string> *proxies)
-      : main_loop_(main_loop), proxies_(proxies) { }
+  ProxyResolver(const std::string& source_url,
+                const std::string& signal_interface,
+                const std::string& signal_name,
+                base::TimeDelta timeout)
+      : source_url_(source_url),
+        signal_interface_(signal_interface),
+        signal_name_(signal_name),
+        timeout_(timeout),
+        weak_ptr_factory_(this),
+        timeout_callback_(base::Bind(&ProxyResolver::HandleBrowserTimeout,
+                                     weak_ptr_factory_.GetWeakPtr())) {}
 
-  void OnSignal(DBusMessage *message) override {
-    // Get args
-    char *source_url = nullptr;
-    char *proxy_list = nullptr;
-    char *error = nullptr;
-    DBusError arg_error;
-    dbus_error_init(&arg_error);
-    if (!dbus_message_get_args(message, &arg_error,
-                               DBUS_TYPE_STRING, &source_url,
-                               DBUS_TYPE_STRING, &proxy_list,
-                               DBUS_TYPE_STRING, &error,
-                               DBUS_TYPE_INVALID)) {
-      LOG(ERROR) << "Error reading D-Bus signal";
-      return;
-    }
-    if (!source_url || !proxy_list) {
-      LOG(ERROR) << "Error getting url, proxy list from D-Bus signal";
+  ~ProxyResolver() override {}
+
+  const std::vector<std::string>& proxies() {
+    return proxies_;
+  }
+
+  int Run() override {
+    // Add task for if the browser proxy call times out.
+    base::MessageLoop::current()->PostDelayedTask(
+        FROM_HERE,
+        timeout_callback_.callback(),
+        timeout_);
+
+    return chromeos::DBusDaemon::Run();
+  }
+
+ protected:
+  // If the browser times out, quit the run loop.
+  void HandleBrowserTimeout() {
+    LOG(ERROR) << "Timeout while waiting for browser to resolve proxy";
+    Quit();
+  }
+
+  // If the signal handler connects successfully, call the browser's
+  // ResolveNetworkProxy D-Bus method.  Otherwise, don't do anything and let
+  // the timeout task quit the run loop.
+  void HandleDBusSignalConnected(const std::string& interface,
+                                 const std::string& signal,
+                                 bool success) {
+    if (!success) {
+      LOG(ERROR) << "Could not connect to signal " << interface << "."
+                 << signal;
+      timeout_callback_.Cancel();
+      Quit();
       return;
     }
 
-    const std::deque<std::string> &proxies = ParseProxyString(proxy_list);
-    for (std::deque<std::string>::const_iterator it = proxies.begin();
-         it != proxies.end(); ++it) {
-      LOG(INFO) << "Found proxy via browser signal: " << (*it).c_str();
-      proxies_->push_back(*it);
-    }
+    chromeos::ErrorPtr error;
+    call_proxy_->ResolveNetworkProxy(source_url_,
+                                     signal_interface_,
+                                     signal_name_,
+                                     &error);
 
-    g_main_loop_quit(main_loop_);
+    if (error) {
+      LOG(ERROR) << "Call to ResolveNetworkProxy failed: "
+                 << error->GetMessage();
+      timeout_callback_.Cancel();
+      Quit();
+    }
+  }
+
+  // Handle incoming ProxyResolved signal.
+  void HandleProxyResolvedSignal(const std::string& source_url,
+                                 const std::string& proxy_info,
+                                 const std::string& error_message) {
+    timeout_callback_.Cancel();
+    proxies_ = ParseProxyString(proxy_info);
+    LOG(INFO) << "Found proxies via browser signal: "
+              << JoinString(proxies_, 'x');
+
+    Quit();
+  }
+
+  int OnInit() override {
+    int return_code = chromeos::DBusDaemon::OnInit();
+    if (return_code != EX_OK)
+      return return_code;
+
+    // Initialize D-Bus proxies.
+    call_proxy_.reset(
+        new org::chromium::LibCrosServiceInterfaceProxy(bus_,
+                                                        kLibCrosServiceName));
+    signal_proxy_.reset(
+        new org::chromium::CrashReporterLibcrosProxyResolvedInterfaceProxy(
+            bus_,
+            kLibCrosServiceName));
+
+    // Set up the D-Bus signal handler.
+    // TODO(crbug.com/446115): Update ResolveNetworkProxy call to use an
+    //     asynchronous return value rather than a return signal.
+    signal_proxy_->RegisterProxyResolvedSignalHandler(
+        base::Bind(&ProxyResolver::HandleProxyResolvedSignal,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&ProxyResolver::HandleDBusSignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    return EX_OK;
   }
 
  private:
-  GMainLoop *main_loop_;
-  std::deque<std::string> *proxies_;
+  unique_ptr<org::chromium::LibCrosServiceInterfaceProxy> call_proxy_;
+  unique_ptr<org::chromium::CrashReporterLibcrosProxyResolvedInterfaceProxy>
+      signal_proxy_;
+
+  const std::string source_url_;
+  const std::string signal_interface_;
+  const std::string signal_name_;
+  base::TimeDelta timeout_;
+
+  std::vector<std::string> proxies_;
+  base::WeakPtrFactory<ProxyResolver> weak_ptr_factory_;
+
+  base::CancelableClosure timeout_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProxyResolver);
 };
 
-static gboolean HandleBrowserTimeout(void *data) {
-  GMainLoop *main_loop = reinterpret_cast<GMainLoop *>(data);
-  LOG(ERROR) << "Timeout while waiting for browser to resolve proxy";
-  g_main_loop_quit(main_loop);
-  return false;  // only call once
-}
+static bool ShowBrowserProxies(std::string url, base::TimeDelta timeout) {
+  // Initialize and run the proxy resolver to watch for signals.
+  ProxyResolver resolver(url,
+                         kLibCrosProxyResolvedSignalInterface,
+                         kLibCrosProxyResolvedName,
+                         timeout);
+  resolver.Run();
 
-static bool ShowBrowserProxies(std::string url, unsigned timeout) {
-  GMainLoop *main_loop = g_main_loop_new(nullptr, false);
+  std::vector<std::string> proxies = resolver.proxies();
 
-  chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection();
-  if (!dbus.HasConnection()) {
-    LOG(ERROR) << "Error connecting to system D-Bus";
-    return false;
-  }
-  chromeos::dbus::Proxy browser_proxy(dbus,
-                                      kLibCrosServiceName,
-                                      kLibCrosServicePath,
-                                      kLibCrosServiceInterface);
-  if (!browser_proxy) {
-    LOG(ERROR) << "Error creating D-Bus proxy to interface "
-               << "'" << kLibCrosServiceName << "'";
-    return false;
-  }
-
-  // Watch for a proxy-resolved signal sent to us
-  std::deque<std::string> proxies;
-  BrowserProxyResolvedSignalWatcher proxy_resolver(main_loop, &proxies);
-  proxy_resolver.StartMonitoring(kLibCrosProxyResolveSignalInterface,
-                                 kLibCrosProxyResolveName);
-
-  // Request the proxies for our URL.  The answer is sent to us via a
-  // proxy-resolved signal.
-  GError *gerror = nullptr;
-  if (!dbus_g_proxy_call(browser_proxy.gproxy(),
-                         kLibCrosServiceResolveNetworkProxyMethodName,
-                         &gerror,
-                         G_TYPE_STRING, url.c_str(),
-                         G_TYPE_STRING, kLibCrosProxyResolveSignalInterface,
-                         G_TYPE_STRING, kLibCrosProxyResolveName,
-                         G_TYPE_INVALID, G_TYPE_INVALID)) {
-    LOG(ERROR) << "Error performing D-Bus proxy call "
-               << "'" << kLibCrosServiceResolveNetworkProxyMethodName << "'"
-               << ": " << GetGErrorMessage(gerror);
-    return false;
-  }
-
-  // Setup a timeout in case the browser doesn't respond with our signal
-  g_timeout_add_seconds(timeout, &HandleBrowserTimeout, main_loop);
-
-  // Loop until we either get the proxy-resolved signal, or until the
-  // timeout is reached.
-  g_main_loop_run(main_loop);
-
-  // If there are no proxies, then we failed to get the proxy-resolved
-  // signal (e.g. timeout was reached).
+  // If proxies is empty, then the timeout was reached waiting for the proxy
+  // resolved signal.  If no proxies are defined, proxies will be populated
+  // with "direct://".
   if (proxies.empty())
     return false;
 
-  for (std::deque<std::string>::const_iterator it = proxies.begin();
-       it != proxies.end(); ++it) {
-    printf("%s\n", (*it).c_str());
+  for (const auto& proxy : proxies) {
+    printf("%s\n", proxy.c_str());
   }
   return true;
 }
 
+}  // namespace
+
 int main(int argc, char *argv[]) {
   CommandLine::Init(argc, argv);
   CommandLine* cl = CommandLine::ForCurrentProcess();
 
-  if (cl->HasSwitch(switches::kHelp)) {
-    LOG(INFO) << switches::kHelpMessage;
+  if (cl->HasSwitch(kHelp)) {
+    LOG(INFO) << kHelpMessage;
     return 0;
   }
 
-  bool quiet = cl->HasSwitch(switches::kQuiet);
-  bool verbose = cl->HasSwitch(switches::kVerbose);
+  bool quiet = cl->HasSwitch(kQuiet);
+  bool verbose = cl->HasSwitch(kVerbose);
 
-  unsigned timeout = switches::kTimeoutDefault;
-  std::string str_timeout = cl->GetSwitchValueASCII(switches::kTimeout);
-  if (!str_timeout.empty() && !base::StringToUint(str_timeout, &timeout)) {
+  int timeout = kTimeoutDefaultSeconds;
+  std::string str_timeout = cl->GetSwitchValueASCII(kTimeout);
+  if (!str_timeout.empty() && !base::StringToInt(str_timeout, &timeout)) {
     LOG(ERROR) << "Invalid timeout value: " << str_timeout;
     return 1;
   }
@@ -236,8 +272,6 @@
     init_flags |= chromeos::kLogToStderr;
   chromeos::InitLog(init_flags);
 
-  ::g_type_init();
-
   std::string url;
   CommandLine::StringVector urls = cl->GetArgs();
   if (!urls.empty()) {
@@ -247,7 +281,7 @@
     LOG(INFO) << "Resolving proxies without URL";
   }
 
-  if (!ShowBrowserProxies(url, timeout)) {
+  if (!ShowBrowserProxies(url, base::TimeDelta::FromSeconds(timeout))) {
     LOG(ERROR) << "Error resolving proxies via the browser";
     LOG(INFO) << "Assuming direct proxy";
     printf("%s\n", kNoProxy);