| // 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/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/startup/startup_dep_impl.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 |