blob: 6be13e158cb30669324aa3fa7e9d4a8ac71a9d41 [file] [log] [blame]
// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "update_engine/postinstall_runner_action.h"
#include <sys/mount.h>
#include <stdlib.h>
#include <vector>
#include "update_engine/action_processor.h"
#include "update_engine/subprocess.h"
#include "update_engine/utils.h"
namespace chromeos_update_engine {
using std::string;
using std::vector;
namespace {
const char kPostinstallScript[] = "/postinst";
}
void PostinstallRunnerAction::PerformAction() {
CHECK(HasInputObject());
install_plan_ = GetInputObject();
const string install_device = install_plan_.install_path;
ScopedActionCompleter completer(processor_, this);
// Make mountpoint.
TEST_AND_RETURN(utils::MakeTempDirectory("/tmp/au_postint_mount.XXXXXX",
&temp_rootfs_dir_));
ScopedDirRemover temp_dir_remover(temp_rootfs_dir_);
unsigned long mountflags = MS_RDONLY;
int rc = mount(install_device.c_str(),
temp_rootfs_dir_.c_str(),
"ext2",
mountflags,
NULL);
// TODO(sosa): Remove once crbug.com/208022 is resolved.
if (rc < 0) {
LOG(INFO) << "Failed to mount install part "
<< install_device << " as ext2. Trying ext3.";
rc = mount(install_device.c_str(),
temp_rootfs_dir_.c_str(),
"ext3",
mountflags,
NULL);
}
if (rc < 0) {
LOG(ERROR) << "Unable to mount destination device " << install_device
<< " onto " << temp_rootfs_dir_;
return;
}
temp_dir_remover.set_should_remove(false);
completer.set_should_complete(false);
if (install_plan_.powerwash_required) {
if (utils::CreatePowerwashMarkerFile(powerwash_marker_file_)) {
powerwash_marker_created_ = true;
} else {
completer.set_code(kErrorCodePostinstallPowerwashError);
return;
}
}
// Runs the postinstall script asynchronously to free up the main loop while
// it's running.
vector<string> command;
command.push_back(temp_rootfs_dir_ + kPostinstallScript);
command.push_back(install_device);
if (!Subprocess::Get().Exec(command, StaticCompletePostinstall, this)) {
CompletePostinstall(1);
}
}
void PostinstallRunnerAction::CompletePostinstall(int return_code) {
ScopedActionCompleter completer(processor_, this);
ScopedTempUnmounter temp_unmounter(temp_rootfs_dir_);
if (return_code != 0) {
LOG(ERROR) << "Postinst command failed with code: " << return_code;
// Undo any changes done to trigger Powerwash using clobber-state.
if (powerwash_marker_created_)
utils::DeletePowerwashMarkerFile(powerwash_marker_file_);
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.
completer.set_code(kErrorCodePostinstallBootedFromFirmwareB);
}
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.
completer.set_code(kErrorCodePostinstallFirmwareRONotUpdatable);
}
return;
}
LOG(INFO) << "Postinst command succeeded";
if (HasOutputPipe()) {
SetOutputObject(install_plan_);
}
completer.set_code(kErrorCodeSuccess);
}
void PostinstallRunnerAction::StaticCompletePostinstall(int return_code,
const string& output,
void* p) {
reinterpret_cast<PostinstallRunnerAction*>(p)->CompletePostinstall(
return_code);
}
} // namespace chromeos_update_engine