Implement Rollback to previously booted partitions.

This CL implements rollback to whatever partition we ran from before.
We expose this functionality via dbus under AttemptRollback and expose
a new command-line option to update_engine_client that a developer can
use.

BUG=chromium:242665
TEST=Unittests, full update, update + rollback and verified.

Change-Id: Ie59f90b9a0b777dc1329592449090c70892236bf
Reviewed-on: https://gerrit.chromium.org/gerrit/58427
Commit-Queue: Chris Sosa <sosa@chromium.org>
Reviewed-by: Chris Sosa <sosa@chromium.org>
Tested-by: Chris Sosa <sosa@chromium.org>
diff --git a/update_engine_client.cc b/update_engine_client.cc
index e28801c..b4a4bda 100644
--- a/update_engine_client.cc
+++ b/update_engine_client.cc
@@ -23,15 +23,17 @@
 using std::string;
 
 DEFINE_string(app_version, "", "Force the current app version.");
-DEFINE_bool(check_for_update, false, "Initiate check for updates.");
-DEFINE_string(omaha_url, "", "The URL of the Omaha update server.");
-DEFINE_bool(reboot, false, "Initiate a reboot if needed.");
-DEFINE_bool(show_channel, false, "Show the current and target channels.");
-DEFINE_bool(status, false, "Print the status to stdout.");
-DEFINE_bool(reset_status, false, "Sets the status in update_engine to idle.");
 DEFINE_string(channel, "",
     "Set the target channel. The device will be powerwashed if the target "
     "channel is more stable than the current channel.");
+DEFINE_bool(check_for_update, false, "Initiate check for updates.");
+DEFINE_string(omaha_url, "", "The URL of the Omaha update server.");
+DEFINE_bool(reboot, false, "Initiate a reboot if needed.");
+DEFINE_bool(reset_status, false, "Sets the status in update_engine to idle.");
+DEFINE_bool(rollback, false, "Perform a rollback to the previous partition.");
+DEFINE_bool(show_channel, false, "Show the current and target channels.");
+DEFINE_bool(powerwash, true, "When performing a rollback, do a powerwash.");
+DEFINE_bool(status, false, "Print the status to stdout.");
 DEFINE_bool(update, false, "Forces an update and waits for its completion. "
             "Exit status is 0 if the update succeeded, and 1 otherwise.");
 DEFINE_bool(watch_for_updates, false,
@@ -176,6 +178,21 @@
   g_main_loop_unref(loop);
 }
 
+bool Rollback(bool rollback) {
+  DBusGProxy* proxy;
+  GError* error = NULL;
+
+  CHECK(GetProxy(&proxy));
+
+  gboolean rc =
+      org_chromium_UpdateEngineInterface_attempt_rollback(proxy,
+                                                          rollback,
+                                                          &error);
+  CHECK_EQ(rc, TRUE) << "Error with rollback request: "
+                     << GetAndFreeGError(&error);
+  return true;
+}
+
 bool CheckForUpdates(const string& app_version, const string& omaha_url) {
   DBusGProxy* proxy;
   GError* error = NULL;
@@ -313,11 +330,27 @@
       LOG(INFO) << "Target Channel (pending update): " << target_channel;
   }
 
+  bool do_update_request = FLAGS_check_for_update | FLAGS_update |
+      !FLAGS_app_version.empty() | !FLAGS_omaha_url.empty();
+
+  if (!FLAGS_powerwash && !FLAGS_rollback) {
+    LOG(FATAL) << "Skipping powerwash only works with rollback";
+    return 1;
+  }
+
+  if (do_update_request && FLAGS_rollback) {
+    LOG(FATAL) << "Update should not be requested with rollback!";
+    return 1;
+  }
+
+  if(FLAGS_rollback) {
+    LOG(INFO) << "Requesting rollback.";
+    CHECK(Rollback(FLAGS_powerwash)) << "Request for rollback failed.";
+    return 0;
+  }
+
   // Initiate an update check, if necessary.
-  if (FLAGS_check_for_update ||
-      FLAGS_update ||
-      !FLAGS_app_version.empty() ||
-      !FLAGS_omaha_url.empty()) {
+  if (do_update_request) {
     LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";
     string app_version = FLAGS_app_version;
     if (FLAGS_update && app_version.empty()) {