blob: 91bee9d32219e1516ac5554987a8931a559f86de [file] [log] [blame]
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "coldboot.h"
#include "coldboot_runner.h"
#include "com_android_ueventd_flags.h"
#include "util.h"
#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <selinux/android.h>
#include <selinux/selinux.h>
#include <sys/stat.h>
namespace flags = com::android::ueventd::flags;
namespace android {
namespace init {
void RestoreconRecurse(const std::string& dir) {
android::base::Timer t;
selinux_android_restorecon(dir.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
// Mark a dir restorecon operation for 50ms,
// Maybe you can add this dir to the ueventd.rc script to parallel processing
if (t.duration() > 50ms) {
LOG(INFO) << "took " << t.duration().count() << "ms restorecon '" << dir.c_str() << "'";
}
}
void ColdBoot::RegenerateUevents() {
uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {
uevent_queue_.emplace_back(uevent);
return ListenerAction::kContinue;
});
}
void ColdBoot::GenerateRestoreCon(const std::string& directory) {
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(directory.c_str()), &closedir);
if (!dir) {
PLOG(WARNING) << "opendir " << directory.c_str();
return;
}
struct dirent* dent;
while ((dent = readdir(dir.get())) != NULL) {
if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) continue;
struct stat st;
if (fstatat(dirfd(dir.get()), dent->d_name, &st, 0) == -1) continue;
if (S_ISDIR(st.st_mode)) {
std::string fullpath = directory + "/" + dent->d_name;
auto parallel_restorecon = std::find(parallel_restorecon_queue_.begin(),
parallel_restorecon_queue_.end(), fullpath);
if (parallel_restorecon == parallel_restorecon_queue_.end()) {
restorecon_queue_.push_back(fullpath);
}
}
}
}
void ColdBoot::Run() {
android::base::Timer cold_boot_timer;
RegenerateUevents();
if (enable_parallel_restorecon_) {
if (parallel_restorecon_queue_.empty()) {
parallel_restorecon_queue_.emplace_back("/sys");
// takes long time for /sys/devices, parallelize it
parallel_restorecon_queue_.emplace_back("/sys/devices");
LOG(INFO) << "Parallel processing directory is not set, set the default";
}
for (const auto& dir : parallel_restorecon_queue_) {
selinux_android_restorecon(dir.c_str(), 0);
GenerateRestoreCon(dir);
}
}
std::unique_ptr<ColdbootRunner> runner;
unsigned int parallelism = std::thread::hardware_concurrency() ?: 4;
if constexpr (flags::enable_threadpool_coldboot()) {
runner = std::make_unique<ColdbootRunnerThreadPool>(
parallelism, uevent_queue_, uevent_handlers_, enable_parallel_restorecon_,
restorecon_queue_);
} else {
runner = std::make_unique<ColdbootRunnerSubprocess>(
parallelism, uevent_queue_, uevent_handlers_, enable_parallel_restorecon_,
restorecon_queue_);
}
runner->StartInBackground();
if (!enable_parallel_restorecon_) {
selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
}
runner->Wait();
android::base::SetProperty(kColdBootDoneProp, "true");
LOG(INFO) << "Coldboot took " << cold_boot_timer.duration().count() / 1000.0f << " seconds";
}
} // namespace init
} // namespace android