blob: 531a9f488ef9472d758f86f253380dad657a441b [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "init/startup/startup_dep_impl.h"
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <memory>
#include <string>
#include <vector>
#include <base/containers/contains.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <brillo/blkdev_utils/storage_utils.h>
#include <brillo/files/file_util.h>
#include <brillo/key_value_store.h>
#include <brillo/process/process.h>
#include <libcrossystem/crossystem.h>
#include <libstorage/platform/platform.h>
#include "init/utils.h"
namespace {
constexpr char kProcCmdLine[] = "proc/cmdline";
constexpr char kFactoryDir[] = "mnt/stateful_partition/dev_image/factory";
} // namespace
namespace startup {
StartupDep::StartupDep(libstorage::Platform* platform) : platform_(platform) {}
int StartupDep::MountEncrypted(const std::vector<std::string>& args,
std::string* output) {
std::unique_ptr<brillo::Process> mount_enc =
platform_->CreateProcessInstance();
mount_enc->AddArg("/usr/sbin/mount-encrypted");
for (auto arg : args) {
mount_enc->AddArg(arg);
}
if (output) {
mount_enc->RedirectOutputToMemory(true);
}
int status = mount_enc->Run();
if (output) {
*output = mount_enc->GetOutputString(STDOUT_FILENO);
}
return status;
}
void StartupDep::BootAlert(const std::string& arg) {
std::unique_ptr<brillo::Process> boot_alert =
platform_->CreateProcessInstance();
boot_alert->AddArg("/sbin/chromeos-boot-alert");
boot_alert->AddArg(arg);
int ret = boot_alert->Run();
if (ret == 0) {
return;
} else if (ret < 0) {
PLOG(ERROR) << "Failed to run chromeos-boot-alert";
} else {
LOG(WARNING) << "chromeos-boot-alert returned non zero exit code: " << ret;
}
}
[[noreturn]] void StartupDep::Clobber(const std::vector<std::string>& args) {
std::unique_ptr<brillo::Process> clobber = platform_->CreateProcessInstance();
clobber->AddArg("/sbin/clobber-state");
// Clobber should not be called with empty args, but to ensure that is
// the case, use "keepimg" if nothing is specified.
if (args.empty()) {
clobber->AddArg("keepimg");
} else {
for (const std::string& arg : args) {
clobber->AddArg(arg);
}
}
int ret = clobber->Run();
CHECK_NE(ret, 0);
PLOG(ERROR) << "unable to run clobber-state; ret=" << ret;
exit(1);
}
void StartupDep::ClobberLog(const std::string& msg) {
std::unique_ptr<brillo::Process> log = platform_->CreateProcessInstance();
log->AddArg("/sbin/clobber-log");
log->AddArg("--");
log->AddArg(msg);
if (log->Run() != 0) {
LOG(WARNING) << "clobber-log failed for message: " << msg;
}
}
void StartupDep::Clobber(const std::string& boot_alert_msg,
const std::vector<std::string>& args,
const std::string& clobber_log_msg) {
BootAlert(boot_alert_msg);
ClobberLog(clobber_log_msg);
Clobber(args);
}
void StartupDep::RemoveInBackground(const std::vector<base::FilePath>& paths) {
pid_t pid = fork();
if (pid == 0) {
for (auto path : paths) {
brillo::DeletePathRecursively(path);
}
exit(0);
}
}
void StartupDep::AddClobberCrashReport(const std::vector<std::string> args) {
std::unique_ptr<brillo::Process> crash = platform_->CreateProcessInstance();
crash->AddArg("/sbin/crash_reporter");
crash->AddArg("--early");
crash->AddArg("--log_to_stderr");
for (auto arg : args) {
crash->AddArg(arg);
}
int ret = crash->Run();
if (ret < 0) {
PLOG(ERROR) << "Failed to run crash_reporter";
return;
} else if (ret != 0) {
LOG(WARNING) << "crash_reporter returned non zero exit code: " << ret;
return;
}
// TODO(sarthakkukreti): Delete this since clobbering handles things.
sync();
}
void StartupDep::ReplayExt4Journal(const base::FilePath& dev) {
std::unique_ptr<brillo::Process> e2fsck = platform_->CreateProcessInstance();
e2fsck->AddArg("/sbin/e2fsck");
e2fsck->AddArg("-p");
e2fsck->AddArg("-E");
e2fsck->AddArg("journal_only");
e2fsck->AddArg(dev.value());
int ret = e2fsck->Run();
if (ret == 0) {
return;
} else if (ret < 0) {
PLOG(WARNING) << "Failed to run e2fsck";
} else {
LOG(WARNING) << "e2fsck returned non zero exit code: " << ret;
}
}
void StartupDep::ClobberLogRepair(const base::FilePath& dev,
const std::string& msg) {
std::unique_ptr<brillo::Process> log_repair =
platform_->CreateProcessInstance();
log_repair->AddArg("/sbin/clobber-log");
log_repair->AddArg("--repair");
log_repair->AddArg(dev.value());
log_repair->AddArg(msg);
int status = log_repair->Run();
if (status == 0) {
return;
} else if (status < 0) {
PLOG(WARNING) << "Failed to run clobber-log";
} else {
LOG(WARNING) << "clobber-log returned non zero exit code: " << status;
}
}
// Returns if we are running on a debug build.
bool IsDebugBuild(crossystem::Crossystem* crossystem) {
std::optional<int> debug =
crossystem->VbGetSystemPropertyInt(crossystem::Crossystem::kDebugBuild);
return debug == 1;
}
// Determine if the device is in dev mode.
bool InDevMode(crossystem::Crossystem* crossystem) {
// cros_debug equals one if we've booted in developer mode or we've booted
// a developer image.
std::optional<int> debug =
crossystem->VbGetSystemPropertyInt(crossystem::Crossystem::kCrosDebug);
return debug == 1;
}
// Determine if the device is using a test image.
bool IsTestImage(libstorage::Platform* platform,
const base::FilePath& lsb_file) {
brillo::KeyValueStore store;
std::string lsb_content;
if (!platform->ReadFileToString(lsb_file, &lsb_content)) {
PLOG(ERROR) << "Problem reading " << lsb_file.value();
return false;
}
if (!store.LoadFromString(lsb_content)) {
PLOG(ERROR) << "Problem parsing " << lsb_file.value();
return false;
}
std::string value;
if (!store.GetString("CHROMEOS_RELEASE_TRACK", &value)) {
PLOG(ERROR) << "CHROMEOS_RELEASE_TRACK not found in " << lsb_file.value();
return false;
}
return base::StartsWith(value, "test", base::CompareCase::SENSITIVE);
}
// Return if the device is in either in factory test mode or in factory
// installer mode.
bool IsFactoryMode(libstorage::Platform* platform,
const base::FilePath& base_dir) {
// The path to factory enabled tag. If this path exists in a debug build,
// we assume factory test mode.
base::FilePath factory_dir = base_dir.Append(kFactoryDir);
base::FilePath factory_tag = factory_dir.Append("enabled");
std::optional<int> res = platform->GetCrosssystem()->VbGetSystemPropertyInt(
crossystem::Crossystem::kDebugBuild);
if (res == 1 && platform->FileExists(factory_tag))
return true;
std::string cmdline;
if (!platform->ReadFileToString(base_dir.Append(kProcCmdLine), &cmdline)) {
PLOG(ERROR) << "Failed to read proc command line";
return false;
}
if (cmdline.find("cros_factory_install") != std::string::npos) {
return true;
}
base::FilePath installer = base_dir.Append("root/.factory_installer");
return platform->FileExists(installer);
}
} // namespace startup