Split payload application code into a subdirectory.

This patch splits from the main libupdate_engine code the part that
is strictly used to download and apply a payload into a new static
library, moving the code to subdirectories. The new library is divided
in two subdirectories: common/ and payload_consumer/, and should not
depend on other update_engine files outside those two subdirectories.
The main difference between those two is that the common/ tools are more
generic and not tied to the payload consumer process, but otherwise they
are both compiled together.

There are still dependencies from the new libpayload_consumer library
into the main directory files and DBus generated files. Those will be
addressed in follow up CLs.

Bug: 25197634
Test: FEATURES=test emerge-link update_engine; `mm` on Brillo.

Change-Id: Id8d0204ea573627e6e26ca9ea17b9592ca95bc23
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
new file mode 100644
index 0000000..33bbf5b
--- /dev/null
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -0,0 +1,187 @@
+//
+// Copyright (C) 2011 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/payload_consumer/postinstall_runner_action.h"
+
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+
+#include "update_engine/common/action_processor.h"
+#include "update_engine/common/subprocess.h"
+#include "update_engine/common/utils.h"
+
+namespace chromeos_update_engine {
+
+using std::string;
+using std::vector;
+
+namespace {
+// The absolute path to the post install command.
+const char kPostinstallScript[] = "/postinst";
+
+// Path to the binary file used by kPostinstallScript. Used to get and log the
+// file format of the binary to debug issues when the ELF format on the update
+// doesn't match the one on the current system. This path is not executed.
+const char kDebugPostinstallBinaryPath[] = "/usr/bin/cros_installer";
+}
+
+void PostinstallRunnerAction::PerformAction() {
+  CHECK(HasInputObject());
+  install_plan_ = GetInputObject();
+
+  if (install_plan_.powerwash_required) {
+    if (utils::CreatePowerwashMarkerFile(powerwash_marker_file_)) {
+      powerwash_marker_created_ = true;
+    } else {
+      return CompletePostinstall(ErrorCode::kPostinstallPowerwashError);
+    }
+  }
+
+  PerformPartitionPostinstall();
+}
+
+void PostinstallRunnerAction::PerformPartitionPostinstall() {
+  // Skip all the partitions that don't have a post-install step.
+  while (current_partition_ < install_plan_.partitions.size() &&
+         !install_plan_.partitions[current_partition_].run_postinstall) {
+    VLOG(1) << "Skipping post-install on partition "
+            << install_plan_.partitions[current_partition_].name;
+    current_partition_++;
+  }
+  if (current_partition_ == install_plan_.partitions.size())
+    return CompletePostinstall(ErrorCode::kSuccess);
+
+  const InstallPlan::Partition& partition =
+      install_plan_.partitions[current_partition_];
+
+  const string mountable_device =
+      utils::MakePartitionNameForMount(partition.target_path);
+  if (mountable_device.empty()) {
+    LOG(ERROR) << "Cannot make mountable device from " << partition.target_path;
+    return CompletePostinstall(ErrorCode::kPostinstallRunnerError);
+  }
+
+  // Perform post-install for the current_partition_ partition. At this point we
+  // need to call CompletePartitionPostinstall to complete the operation and
+  // cleanup.
+  TEST_AND_RETURN(
+      utils::MakeTempDirectory("au_postint_mount.XXXXXX", &temp_rootfs_dir_));
+
+  if (!utils::MountFilesystem(mountable_device, temp_rootfs_dir_, MS_RDONLY)) {
+    return CompletePartitionPostinstall(
+        1, "Error mounting the device " + mountable_device);
+  }
+
+  LOG(INFO) << "Performing postinst (" << kPostinstallScript
+            << ") installed on device " << partition.target_path
+            << " and mountable device " << mountable_device;
+
+  // Logs the file format of the postinstall script we are about to run. This
+  // will help debug when the postinstall script doesn't match the architecture
+  // of our build.
+  LOG(INFO) << "Format file for new " <<  kPostinstallScript << " is: "
+            << utils::GetFileFormat(temp_rootfs_dir_ + kPostinstallScript);
+  LOG(INFO) << "Format file for new " <<  kDebugPostinstallBinaryPath << " is: "
+            << utils::GetFileFormat(
+                temp_rootfs_dir_ + kDebugPostinstallBinaryPath);
+
+  // Runs the postinstall script asynchronously to free up the main loop while
+  // it's running.
+  vector<string> command;
+  if (!install_plan_.download_url.empty()) {
+    command.push_back(temp_rootfs_dir_ + kPostinstallScript);
+  } else {
+    // TODO(sosa): crbug.com/366207.
+    // If we're doing a rollback, just run our own postinstall.
+    command.push_back(kPostinstallScript);
+  }
+  command.push_back(partition.target_path);
+  if (!Subprocess::Get().Exec(
+          command,
+          base::Bind(
+              &PostinstallRunnerAction::CompletePartitionPostinstall,
+              base::Unretained(this)))) {
+    CompletePartitionPostinstall(1, "Postinstall didn't launch");
+  }
+}
+
+void PostinstallRunnerAction::CompletePartitionPostinstall(
+    int return_code,
+    const string& output) {
+  utils::UnmountFilesystem(temp_rootfs_dir_);
+  if (!base::DeleteFile(base::FilePath(temp_rootfs_dir_), false)) {
+    PLOG(WARNING) << "Not removing mountpoint " << temp_rootfs_dir_;
+  }
+  temp_rootfs_dir_.clear();
+
+  if (return_code != 0) {
+    LOG(ERROR) << "Postinst command failed with code: " << return_code;
+    ErrorCode error_code = ErrorCode::kPostinstallRunnerError;
+
+    if (return_code == 3) {
+      // This special return code means that we tried to update firmware,
+      // but couldn't because we booted from FW B, and we need to reboot
+      // to get back to FW A.
+      error_code = ErrorCode::kPostinstallBootedFromFirmwareB;
+    }
+
+    if (return_code == 4) {
+      // This special return code means that we tried to update firmware,
+      // but couldn't because we booted from FW B, and we need to reboot
+      // to get back to FW A.
+      error_code = ErrorCode::kPostinstallFirmwareRONotUpdatable;
+    }
+    return CompletePostinstall(error_code);
+  }
+  current_partition_++;
+  PerformPartitionPostinstall();
+}
+
+void PostinstallRunnerAction::CompletePostinstall(ErrorCode error_code) {
+  // We only attempt to mark the new slot as active if all the postinstall
+  // steps succeeded.
+  if (error_code == ErrorCode::kSuccess &&
+      !system_state_->boot_control()->SetActiveBootSlot(
+          install_plan_.target_slot)) {
+    error_code = ErrorCode::kPostinstallRunnerError;
+  }
+
+  ScopedActionCompleter completer(processor_, this);
+
+  if (error_code != ErrorCode::kSuccess) {
+    LOG(ERROR) << "Postinstall action failed.";
+
+    // Undo any changes done to trigger Powerwash using clobber-state.
+    if (powerwash_marker_created_)
+      utils::DeletePowerwashMarkerFile(powerwash_marker_file_);
+
+    return;
+  }
+
+  LOG(INFO) << "All post-install commands succeeded";
+  if (HasOutputPipe()) {
+    SetOutputObject(install_plan_);
+  }
+
+  completer.set_code(ErrorCode::kSuccess);
+}
+
+}  // namespace chromeos_update_engine