blob: f87c35ea0d13614dbf7c0a84a494df1484616863 [file] [edit]
// 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 <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/statvfs.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/lvm.h>
#include <brillo/files/file_util.h>
#include <brillo/key_value_store.h>
#include <brillo/process/process.h>
#include "init/clobber_state.h"
#include "init/crossystem.h"
#include "init/crossystem_impl.h"
#include "init/startup/platform_impl.h"
#include "init/utils.h"
namespace {
constexpr char kProcCmdLine[] = "proc/cmdline";
constexpr char kFactoryDir[] = "mnt/stateful_partition/dev_image/factory";
constexpr char kProcFilesystems[] = "proc/filesystems";
const size_t kMaxReadSize = 4 * 1024;
} // namespace
namespace startup {
bool Platform::Stat(const base::FilePath& path, struct stat* st) {
return stat(path.value().c_str(), st) == 0;
}
bool Platform::Statvfs(const base::FilePath& path, struct statvfs* st) {
return statvfs(path.value().c_str(), st) == 0;
}
bool Platform::Mount(const base::FilePath& src,
const base::FilePath& dst,
const std::string& type,
const unsigned long flags, // NOLINT(runtime/int)
const std::string& data) {
return mount(src.value().c_str(), dst.value().c_str(), type.c_str(), flags,
data.c_str()) == 0;
}
bool Platform::Mount(const std::string& src,
const base::FilePath& dst,
const std::string& type,
const unsigned long flags, // NOLINT(runtime/int)
const std::string& data) {
return mount(src.c_str(), dst.value().c_str(), type.c_str(), flags,
data.c_str()) == 0;
}
bool Platform::Umount(const base::FilePath& path) {
return !umount(path.value().c_str());
}
base::ScopedFD Platform::Open(const base::FilePath& pathname, int flags) {
return base::ScopedFD(HANDLE_EINTR(open(pathname.value().c_str(), flags)));
}
// NOLINTNEXTLINE(runtime/int)
int Platform::Ioctl(int fd, unsigned long request, int* arg1) {
return ioctl(fd, request, arg1);
}
int Platform::MountEncrypted(const std::vector<std::string>& args,
std::string* output) {
brillo::ProcessImpl mount_enc;
base::FilePath file;
base::CreateTemporaryFile(&file);
mount_enc.AddArg("/usr/sbin/mount-encrypted");
for (auto arg : args) {
mount_enc.AddArg(arg);
}
mount_enc.RedirectOutput(file.value());
int status = mount_enc.Run();
if (status == 0) {
base::ReadFileToString(file, output);
}
brillo::DeleteFile(file);
return status;
}
void Platform::BootAlert(const std::string& arg) {
brillo::ProcessImpl boot_alert;
boot_alert.AddArg("/sbin/chromeos-boot-alert");
boot_alert.AddArg(arg);
int ret = boot_alert.Run();
if (ret != 0) {
PLOG(WARNING) << "chromeos-boot-alert failed with code " << ret;
}
}
[[noreturn]] void Platform::Clobber(const std::vector<std::string> args) {
brillo::ProcessImpl clobber;
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);
}
bool Platform::VpdSlow(const std::vector<std::string>& args,
std::string* output) {
brillo::ProcessImpl vpd;
vpd.AddArg("/usr/sbin/vpd");
for (const std::string& arg : args) {
vpd.AddArg(arg);
}
vpd.RedirectUsingMemory(STDOUT_FILENO);
if (vpd.Run() == 0) {
*output = vpd.GetOutputString(STDOUT_FILENO);
return true;
}
return false;
}
void Platform::ClobberLog(const std::string& msg) {
brillo::ProcessImpl log;
log.AddArg("/sbin/clobber-log");
log.AddArg("--");
log.AddArg(msg);
if (log.Run() != 0) {
LOG(WARNING) << "clobber-log failed for message: " << msg;
}
}
void Platform::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 Platform::RemoveInBackground(const std::vector<base::FilePath>& paths) {
pid_t pid = fork();
if (pid == 0) {
for (auto path : paths) {
brillo::DeletePathRecursively(path);
}
exit(0);
}
}
// Run command, cmd_path.
void Platform::RunProcess(const base::FilePath& cmd_path) {
brillo::ProcessImpl proc;
proc.AddArg(cmd_path.value());
if (proc.Run() != 0) {
PLOG(WARNING) << "Failed to run " << cmd_path.value();
}
}
bool Platform::RunHiberman(const base::FilePath& output_file) {
brillo::ProcessImpl hiberman;
hiberman.AddArg("/usr/sbin/hiberman");
hiberman.AddArg("resume-init");
hiberman.AddArg("-v");
hiberman.RedirectOutput(output_file.value());
int ret = hiberman.Run();
if (ret != 0) {
PLOG(WARNING) << "hiberman failed with code " << ret;
return false;
}
return true;
}
void Platform::AddClobberCrashReport(const std::vector<std::string> args) {
brillo::ProcessImpl crash;
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(WARNING) << "crash_reporter failed with code " << ret;
return;
}
// TODO(sarthakkukreti): Delete this since clobbering handles things.
sync();
}
void Platform::ReplayExt4Journal(const base::FilePath& dev) {
brillo::ProcessImpl e2fsck;
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) {
PLOG(WARNING) << "e2fsck failed with code " << ret;
}
}
void Platform::ClobberLogRepair(const base::FilePath& dev,
const std::string& msg) {
brillo::ProcessImpl log_repair;
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) {
PLOG(WARNING) << "Repairing clobber.log failed with code " << status;
}
}
// Returns if we are running on a debug build.
bool Platform::IsDebugBuild(CrosSystem* const cros_system) {
int debug;
if (cros_system->GetInt("debug_build", &debug) && debug == 1) {
return true;
} else {
return false;
}
}
// Determine if the device is in dev mode.
bool Platform::InDevMode(CrosSystem* cros_system) {
// cros_debug equals one if we've booted in developer mode or we've booted
// a developer image.
int debug;
return (cros_system->GetInt("cros_debug", &debug) && debug == 1);
}
// Determine if the device is using a test image.
bool IsTestImage(const base::FilePath& lsb_file) {
brillo::KeyValueStore store;
if (!store.Load(lsb_file)) {
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 factory test mode.
bool IsFactoryTestMode(CrosSystem* cros_system,
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");
struct stat statbuf;
int res;
if (cros_system->GetInt("debug_build", &res) && res == 1 &&
stat(factory_tag.value().c_str(), &statbuf) == 0 &&
S_ISREG(statbuf.st_mode)) {
return true;
}
return false;
}
// Return if the device is in factory installer mode.
bool IsFactoryInstallerMode(const base::FilePath& base_dir) {
std::string cmdline;
if (!base::ReadFileToStringWithMaxSize(base_dir.Append(kProcCmdLine),
&cmdline, kMaxReadSize)) {
PLOG(ERROR) << "Failed to read proc command line";
return false;
}
if (cmdline.find("cros_factory_install") != std::string::npos) {
return true;
}
struct stat statbuf;
base::FilePath installer = base_dir.Append("root/.factory_installer");
if (stat(installer.value().c_str(), &statbuf) == 0 &&
S_ISREG(statbuf.st_mode)) {
return true;
}
return false;
}
// Return if the device is in either in factory test mode or in factory
// installer mode.
bool IsFactoryMode(CrosSystem* cros_system, const base::FilePath& base_dir) {
return (IsFactoryTestMode(cros_system, base_dir) ||
IsFactoryInstallerMode(base_dir));
}
// Determines if a filesystem is supported.
// False if there's an error checking or if the filesystem isn't supported,
// true if the filesystem is supported.
bool IsSupportedFilesystem(const std::string& filesystem,
const base::FilePath& base_dir) {
// List of supported filesystems, along with indicators like "nodev".
// See /proc/filesystems under `man 5 proc`.
const base::FilePath filesystems = base_dir.Append(kProcFilesystems);
std::string filesystems_content;
if (!base::ReadFileToString(filesystems, &filesystems_content)) {
PLOG(ERROR) << "Failed to read " << kProcFilesystems;
return false;
}
const auto supported_filesystems =
base::SplitStringPiece(filesystems_content, base::kWhitespaceASCII,
base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
return base::Contains(supported_filesystems, filesystem);
}
} // namespace startup