update_engine: Added more logging and debugging for rollback checking

To help troubleshoot issues similar to http://crbug.com/356975 I added
more logging in DBus methods of update_engine to trace various stages
of determining available boot partitions, etc.

Also added two more DBus methods - to get the suggested rollback
partition name (and switched CanRollback to use this method) and
the list of availavle kernel partitions along with the 'bootable'
flag for each.

Changed update_engine_client to show the name of avaiable rollback
partition with --can_rollback and also added --show_kernels to
output list of available kernel partitions and whether each partition
is bootable or not.

BUG=None
TEST=Unit tests pass

Change-Id: Ib7f92a6460c578953ea1ba9b23bd0669acb0e22f
Reviewed-on: https://chromium-review.googlesource.com/191949
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/UpdateEngine.conf b/UpdateEngine.conf
index bdfd3c8..e9fe2d5 100644
--- a/UpdateEngine.conf
+++ b/UpdateEngine.conf
@@ -20,6 +20,12 @@
            send_member="CanRollback"/>
     <allow send_destination="org.chromium.UpdateEngine"
            send_interface="org.chromium.UpdateEngineInterface"
+           send_member="GetRollbackPartition"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="GetKernelDevices"/>
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
            send_member="ResetStatus"/>
     <allow send_destination="org.chromium.UpdateEngine"
            send_interface="org.chromium.UpdateEngineInterface"
@@ -50,7 +56,7 @@
            send_member="GetDurationSinceUpdate"/>
     <allow send_destination="org.chromium.UpdateEngine"
            send_interface="org.chromium.UpdateEngineInterface"
-           send_member="GetOldVersion"/>
+           send_member="GetPrevVersion"/>
     <allow send_interface="org.chromium.UpdateEngineLibcrosProxyResolvedInterface" />
   </policy>
   <policy user="power">
diff --git a/dbus_service.cc b/dbus_service.cc
index 7235be1..03bc602 100644
--- a/dbus_service.cc
+++ b/dbus_service.cc
@@ -8,6 +8,7 @@
 #include <string>
 
 #include <base/logging.h>
+#include <base/strings/stringprintf.h>
 #include <policy/device_policy.h>
 
 #include "update_engine/clock_interface.h"
@@ -196,8 +197,33 @@
                                             gboolean* out_can_rollback,
                                             GError **error)
 {
-  LOG(INFO) << "Checking for a rollback partition.";
-  *out_can_rollback = self->system_state_->update_attempter()->CanRollback();
+  bool can_rollback = self->system_state_->update_attempter()->CanRollback();
+  LOG(INFO) << "Checking for a rollback partition. Result: " << can_rollback;
+  *out_can_rollback = can_rollback;
+  return TRUE;
+}
+
+gboolean update_engine_service_get_rollback_partition(
+    UpdateEngineService* self,
+    gchar** out_rollback_partition_name,
+    GError **error) {
+  auto name = self->system_state_->update_attempter()->GetRollbackPartition();
+  LOG(INFO) << "Getting rollback partition name. Result: " << name;
+  *out_rollback_partition_name = g_strdup(name.c_str());
+  return TRUE;
+}
+
+gboolean update_engine_service_get_kernel_devices(UpdateEngineService* self,
+                                                  gchar** out_kernel_devices,
+                                                  GError **error) {
+  auto devices = self->system_state_->update_attempter()->GetKernelDevices();
+  std::string info;
+  for (auto&& device : devices) {
+    base::StringAppendF(&info, "%d:%s\n",
+                        device.second ? 1 : 0, device.first.c_str());
+  }
+  LOG(INFO) << "Available kernel devices: " << info;
+  *out_kernel_devices = g_strdup(info.c_str());
   return TRUE;
 }
 
diff --git a/dbus_service.h b/dbus_service.h
index 3449f1f..3507918 100644
--- a/dbus_service.h
+++ b/dbus_service.h
@@ -69,6 +69,18 @@
     gboolean* out_can_rollback,
     GError **error);
 
+// Returns the name of kernel partition that can be rolled back into.
+gboolean update_engine_service_get_rollback_partition(
+  UpdateEngineService* self,
+  gchar** out_rollback_partition_name,
+  GError **error);
+
+// Returns a list of available kernel partitions and whether each of them
+// can be booted from or not.
+gboolean update_engine_service_get_kernel_devices(UpdateEngineService* self,
+                                                  gchar** out_kernel_devices,
+                                                  GError **error);
+
 gboolean update_engine_service_reset_status(UpdateEngineService* self,
                                             GError **error);
 
diff --git a/hardware.cc b/hardware.cc
index 44bc9de..f094d97 100644
--- a/hardware.cc
+++ b/hardware.cc
@@ -86,10 +86,7 @@
   }
 
   std::vector<std::string> devices;
-  const int slot_count = 2; // Use only partition slots A and B
-  devices.reserve(slot_count);
-  for(int slot = 0; slot < slot_count; slot++) {
-    int partition_num = (slot + 1) * 2; // for now, only #2, #4
+  for (int partition_num : {2, 4}) { // for now, only #2, #4 for slot A & B
     std::string device = utils::MakePartitionName(disk_name, partition_num);
     if(!device.empty()) {
       devices.push_back(std::move(device));
diff --git a/update_attempter.cc b/update_attempter.cc
index d5c2d98..e71d104 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -785,19 +785,28 @@
 }
 
 bool UpdateAttempter::CanRollback() const {
+  return !GetRollbackPartition().empty();
+}
+
+std::string UpdateAttempter::GetRollbackPartition() const {
   std::vector<std::string> kernel_devices =
       system_state_->hardware()->GetKernelDevices();
 
   std::string boot_kernel_device =
       system_state_->hardware()->BootKernelDevice();
 
+  LOG(INFO) << "UpdateAttempter::GetRollbackPartition";
+  for (auto&& name : kernel_devices)
+    LOG(INFO) << "  Available kernel device = " << name;
+  LOG(INFO) << "  Boot kernel device =      " << boot_kernel_device;
+
   auto current = std::find(kernel_devices.begin(), kernel_devices.end(),
                            boot_kernel_device);
 
   if(current == kernel_devices.end()) {
     LOG(ERROR) << "Unable to find the boot kernel device in the list of "
                << "available devices";
-    return false;
+    return std::string();
   }
 
   for (std::string const& device_name : kernel_devices) {
@@ -805,12 +814,35 @@
       bool bootable = false;
       if (system_state_->hardware()->IsKernelBootable(device_name, &bootable) &&
           bootable) {
-        return true;
+        return device_name;
       }
     }
   }
 
-  return false;
+  return std::string();
+}
+
+std::vector<std::pair<std::string, bool>>
+    UpdateAttempter::GetKernelDevices() const {
+  std::vector<std::string> kernel_devices =
+    system_state_->hardware()->GetKernelDevices();
+
+  std::string boot_kernel_device =
+    system_state_->hardware()->BootKernelDevice();
+
+  std::vector<std::pair<std::string, bool>> info_list;
+  info_list.reserve(kernel_devices.size());
+
+  for (std::string device_name : kernel_devices) {
+    bool bootable = false;
+    system_state_->hardware()->IsKernelBootable(device_name, &bootable);
+    // Add '*' to the name of the partition we booted from.
+    if (device_name == boot_kernel_device)
+      device_name += '*';
+    info_list.emplace_back(device_name, bootable);
+  }
+
+  return info_list;
 }
 
 void UpdateAttempter::CheckForUpdate(const string& app_version,
diff --git a/update_attempter.h b/update_attempter.h
index d76d168..d64771b 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -153,6 +153,15 @@
   // partition exists.
   bool CanRollback() const;
 
+  // This is the internal entry point for getting a rollback partition name,
+  // if one exists. It returns the bootable rollback kernel device partition
+  // name or empty string if none is available.
+  std::string GetRollbackPartition() const;
+
+  // Returns a list of available kernel partitions along with information
+  // whether it is possible to boot from it.
+  std::vector<std::pair<std::string, bool>> GetKernelDevices() const;
+
   // Initiates a reboot if the current state is
   // UPDATED_NEED_REBOOT. Returns true on sucess, false otherwise.
   bool RebootIfNeeded();
diff --git a/update_engine.xml b/update_engine.xml
index 3891374..b99f345 100644
--- a/update_engine.xml
+++ b/update_engine.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="UTF-8" ?>
+<?xml version="1.0" encoding="utf-8" ?>
 <!-- COPYRIGHT HERE
      dbus-binding-tool -mode=glib-server -prefix=update_engine update_engine.xml
                         &gt; glib_server.h
@@ -81,5 +81,11 @@
     <method name="GetPrevVersion">
       <arg type="s" name="prev_version" direction="out" />
     </method>
+    <method name="GetKernelDevices">
+      <arg type="s" name="kernel_devices" direction="out" />
+    </method>
+    <method name="GetRollbackPartition">
+      <arg type="s" name="rollback_partition_name" direction="out" />
+    </method>
   </interface>
 </node>
diff --git a/update_engine_client.cc b/update_engine_client.cc
index d27f027..3ee2a2a 100644
--- a/update_engine_client.cc
+++ b/update_engine_client.cc
@@ -59,6 +59,8 @@
             "Listen for status updates and print them to the screen.");
 DEFINE_bool(prev_version, false,
             "Show the previous OS version used before the update reboot.");
+DEFINE_bool(show_kernels, false, "Show the list of kernel patritions and "
+            "whether each of them is bootable or not");
 
 namespace {
 
@@ -211,20 +213,40 @@
   return true;
 }
 
-bool CanRollback() {
+std::string GetRollbackPartition() {
   DBusGProxy* proxy;
   GError* error = NULL;
 
   CHECK(GetProxy(&proxy));
 
-  gboolean can_rollback = FALSE;
-  gboolean rc = update_engine_client_can_rollback(proxy,
-                                                  &can_rollback,
-                                                  &error);
+  char* rollback_partition = nullptr;
+  gboolean rc = update_engine_client_get_rollback_partition(proxy,
+                                                            &rollback_partition,
+                                                            &error);
   CHECK_EQ(rc, TRUE) << "Error while querying rollback partition availabilty: "
                      << GetAndFreeGError(&error);
-  return can_rollback;
+  std::string partition = rollback_partition;
+  g_free(rollback_partition);
+  return partition;
 }
+
+std::string GetKernelDevices() {
+  DBusGProxy* proxy;
+  GError* error = nullptr;
+
+  CHECK(GetProxy(&proxy));
+
+  char* kernel_devices = nullptr;
+  gboolean rc = update_engine_client_get_kernel_devices(proxy,
+                                                        &kernel_devices,
+                                                        &error);
+  CHECK_EQ(rc, TRUE) << "Error while getting a list of kernel devices: "
+    << GetAndFreeGError(&error);
+  std::string devices = kernel_devices;
+  g_free(kernel_devices);
+  return devices;
+}
+
 bool CheckForUpdates(const string& app_version, const string& omaha_url) {
   DBusGProxy* proxy;
   GError* error = NULL;
@@ -452,9 +474,13 @@
 
   // Show the rollback availability.
   if (FLAGS_can_rollback) {
-    bool can_rollback = CanRollback();
-    LOG(INFO) << "Rollback partition: "
-              << (can_rollback ? "AVAILABLE" : "UNAVAILABLE");
+    std::string rollback_partition = GetRollbackPartition();
+    if (rollback_partition.empty())
+      rollback_partition = "UNAVAILABLE";
+    else
+      rollback_partition = "AVAILABLE: " + rollback_partition;
+
+    LOG(INFO) << "Rollback partition: " << rollback_partition;
   }
 
   // Show the current P2P enabled setting.
@@ -547,6 +573,11 @@
     ShowPrevVersion();
   }
 
+  if (FLAGS_show_kernels) {
+    LOG(INFO) << "Kernel partitions:\n"
+              << GetKernelDevices();
+  }
+
   LOG(INFO) << "Done.";
   return 0;
 }