blob: 16ac43b4bb2f12d51510bfa30eccdcf7aa494aa2 [file] [log] [blame]
// Copyright 2016 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 "login_manager/vpd_process_impl.h"
#include <string>
#include <vector>
#include <base/callback_helpers.h>
#include <base/strings/string_util.h>
#include <base/sys_info.h>
#include <chromeos/dbus/service_constants.h>
#include "login_manager/dbus_util.h"
#include "metrics/metrics_library.h"
namespace {
constexpr char kVpdUpdateMetric[] = "Enterprise.VpdUpdateStatus";
// Convenience function to get the board name and remove "-signed.." if present.
// The output is converted to lower-case. Returns "unknown" if
// CHROMEOS_RELEASE_BOARD is not set.
// TODO(igorcov): Remove this when similar function appears in libchrome.
std::string GetStrippedReleaseBoard() {
std::string board = base::SysInfo::GetLsbReleaseBoard();
const size_t index = board.find("-signed-");
if (index != std::string::npos) {
board.resize(index);
}
return base::ToLowerASCII(board);
}
} // namespace
namespace login_manager {
VpdProcessImpl::VpdProcessImpl(SystemUtils* system_utils)
: system_utils_(system_utils) {
DCHECK(system_utils_);
}
bool VpdProcessImpl::RunInBackground(
const std::vector<std::string>& flags,
const std::vector<int>& values,
bool is_enrolled,
const PolicyService::Completion& completion) {
DCHECK(flags.size() == values.size());
subprocess_.reset(new ChildJobInterface::Subprocess(0, system_utils_));
is_enrolled_ = is_enrolled;
std::vector<std::string> argv;
argv.push_back("/usr/sbin/set_binary_flag_vpd");
for (size_t i = 0; i < flags.size(); i++) {
argv.push_back(flags[i]);
argv.push_back(std::to_string(values[i]));
}
if (!subprocess_->ForkAndExec(argv, std::vector<std::string>())) {
// The caller remains responsible for running |completion|.
return false;
}
// |completion_| will be run when the job exits.
completion_ = completion;
return true;
}
bool VpdProcessImpl::IsManagedJob(pid_t pid) {
return subprocess_ && subprocess_->pid() > 0 && subprocess_->pid() == pid;
}
void VpdProcessImpl::RequestJobExit() {
if (subprocess_ && subprocess_->pid() > 0)
subprocess_->Kill(SIGTERM);
}
void VpdProcessImpl::EnsureJobExit(base::TimeDelta timeout) {
if (subprocess_) {
if (subprocess_->pid() < 0)
return;
if (!system_utils_->ProcessGroupIsGone(subprocess_->pid(), timeout)) {
subprocess_->KillEverything(SIGABRT);
DLOG(INFO) << "Child process was killed.";
}
}
}
void VpdProcessImpl::HandleExit(const siginfo_t& info) {
MetricsLibrary metrics;
metrics.Init();
metrics.SendSparseToUMA(kVpdUpdateMetric, info.si_status);
const bool success = (info.si_status == 0);
LOG_IF(ERROR, !success) << "Failed to update VPD, code = " << info.si_status;
if (completion_.is_null())
return;
// Reset the completion to ensure we won't call it again.
auto completion = base::ResetAndReturn(&completion_);
// We have to notify Chrome that the process has finished. Ignore the VPD
// update error if the device is not enrolled.
if (success || !is_enrolled_) {
completion.Run(brillo::ErrorPtr());
return;
}
// TODO(igorcov): Remove the exception when crbug.com/653814 is fixed.
const std::string board_name = GetStrippedReleaseBoard();
if (board_name == "parrot" || board_name == "glimmer") {
LOG(ERROR) << "Failed to update VPD, but error ignored for device: "
<< board_name;
completion.Run(brillo::ErrorPtr());
return;
}
LOG(ERROR) << "The device failed to update VPD: "
<< board_name
<< ", full board name: "
<< base::SysInfo::GetLsbReleaseBoard();
completion.Run(CreateError(
dbus_error::kVpdUpdateFailed, "Failed to update VPD"));
}
} // namespace login_manager