update_engine: Wait for dbus connection to be available.

In the general case, update_engine won't be launched until dbus-daemon
is ready. Nevertheless, if dbus-daemon crashes for some reason,
update_engine will crash and restart again and again because it can't
get the DBus system bus. This will generate many crash reports in a
short period of time.

This patch waits up to 10 seconds to get the DBus system bus, and then
crashes if it couldn't get it by then.

BUG=chromium:417142
TEST=launch update_engine without dbus-daemon and saw it retry and crash.
TEST=launch update_engine in the normal case, it doesn't retry..

Change-Id: I99714cec87f99eeef385fc6e8787334fbc029044
Reviewed-on: https://chromium-review.googlesource.com/219690
Reviewed-by: David Zeuthen <[email protected]>
Commit-Queue: Alex Deymo <[email protected]>
Tested-by: Alex Deymo <[email protected]>
diff --git a/main.cc b/main.cc
index 7b92efa..3242c3d 100644
--- a/main.cc
+++ b/main.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <unistd.h>
+
 #include <string>
 #include <vector>
 
@@ -11,6 +13,7 @@
 #include <base/logging.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
+#include <base/time/time.h>
 #include <gflags/gflags.h>
 #include <glib.h>
 #include <metrics/metrics_library.h>
@@ -39,6 +42,10 @@
 using std::string;
 using std::vector;
 
+namespace {
+const int kDBusSystemMaxWaitSeconds = 2 * 60;
+}  // namespace
+
 namespace chromeos_update_engine {
 
 gboolean UpdateBootFlags(void* arg) {
@@ -58,7 +65,29 @@
 
 namespace {
 
-void SetupDbusService(UpdateEngineService* service) {
+// Wait for DBus to be ready by attempting to get the system bus up to
+// |timeout| time. Returns whether it succeeded to get the bus.
+bool WaitForDBusSystem(base::TimeDelta timeout) {
+  GError *error = nullptr;
+  DBusGConnection *bus = nullptr;
+  Clock clock;
+  base::Time deadline = clock.GetMonotonicTime() + timeout;
+
+  while (clock.GetMonotonicTime() < deadline) {
+    bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
+    if (bus)
+      return true;
+    LOG(WARNING) << "Failed to get system bus, waiting: "
+                 << utils::GetAndFreeGError(&error);
+    // Wait 1 second.
+    sleep(1);
+  }
+  LOG(ERROR) << "Failed to get system bus after " << timeout.InSeconds()
+             << " seconds.";
+  return false;
+}
+
+void SetupDBusService(UpdateEngineService* service) {
   DBusGConnection *bus;
   DBusGProxy *proxy;
   GError *error = nullptr;
@@ -171,6 +200,11 @@
   // Create the single GMainLoop
   GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
 
+  // Wait up to 2 minutes for DBus to be ready.
+  LOG_IF(FATAL, !chromeos_update_engine::WaitForDBusSystem(
+      base::TimeDelta::FromSeconds(kDBusSystemMaxWaitSeconds)))
+      << "Failed to initialize DBus, aborting.";
+
   chromeos_update_engine::RealSystemState real_system_state;
   LOG_IF(ERROR, !real_system_state.Initialize())
       << "Failed to initialize system state.";
@@ -191,7 +225,7 @@
   UpdateEngineService* service = update_engine_service_new();
   service->system_state_ = &real_system_state;
   update_attempter->set_dbus_service(service);
-  chromeos_update_engine::SetupDbusService(service);
+  chromeos_update_engine::SetupDBusService(service);
 
   // Initiate update checks.
   update_attempter->ScheduleUpdates();