blob: c7d891944a4fc70480c06362d60bb4e18a9f8730 [file] [log] [blame]
/*
* Copyright (C) 2018 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.
*/
#define LOG_TAG "apexd"
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <selinux/android.h>
#include <strings.h>
#include <sys/stat.h>
#include <memory>
#include "apexd.h"
#include "apexd_checkpoint_vold.h"
#include "apexd_lifecycle.h"
#include "apexservice.h"
namespace {
using android::base::SetDefaultTag;
int HandleSubcommand(int argc, char** argv) {
if (strcmp("--bootstrap", argv[1]) == 0) {
SetDefaultTag("apexd-bootstrap");
return android::apex::OnBootstrap();
}
if (strcmp("--unmount-all", argv[1]) == 0) {
SetDefaultTag("apexd-unmount-all");
bool also_include_staged_apexes =
argc >= 3 && strcmp("--also-include-staged-apexes", argv[2]) == 0;
std::unique_ptr<android::apex::ApexSessionManager> session_manager;
if (also_include_staged_apexes) {
session_manager = android::apex::ApexSessionManager::Create(
android::apex::GetSessionsDir());
android::apex::InitializeSessionManager(session_manager.get());
}
return android::apex::UnmountAll(also_include_staged_apexes);
}
if (strcmp("--otachroot-bootstrap", argv[1]) == 0) {
SetDefaultTag("apexd-otachroot");
bool also_include_staged_apexes =
argc >= 3 && strcmp("--also-include-staged-apexes", argv[2]) == 0;
std::unique_ptr<android::apex::ApexSessionManager> session_manager;
if (also_include_staged_apexes) {
session_manager = android::apex::ApexSessionManager::Create(
android::apex::GetSessionsDir());
android::apex::InitializeSessionManager(session_manager.get());
}
return android::apex::OnOtaChrootBootstrap(also_include_staged_apexes);
}
if (strcmp("--snapshotde", argv[1]) == 0) {
SetDefaultTag("apexd-snapshotde");
// Need to know if checkpointing is enabled so that a prerestore snapshot
// can be taken if it's not.
android::base::Result<android::apex::VoldCheckpointInterface>
vold_service_st = android::apex::VoldCheckpointInterface::Create();
if (!vold_service_st.ok()) {
LOG(ERROR) << "Could not retrieve vold service: "
<< vold_service_st.error();
} else {
android::apex::InitializeVold(&*vold_service_st);
}
// We are running regular apexd, which starts after /metadata/apex/sessions
// and /data/apex/sessions have been created by init. It is safe to create
// ApexSessionManager.
auto session_manager = android::apex::ApexSessionManager::Create(
android::apex::GetSessionsDir());
android::apex::InitializeSessionManager(session_manager.get());
int result = android::apex::SnapshotOrRestoreDeUserData();
if (result == 0) {
// Notify other components (e.g. init) that all APEXs are ready to be used
// Note that it's important that the binder service is registered at this
// point, since other system services might depend on it.
android::apex::OnAllPackagesReady();
}
return result;
}
if (strcmp("--vm", argv[1]) == 0) {
SetDefaultTag("apexd-vm");
return android::apex::OnStartInVmMode();
}
LOG(ERROR) << "Unknown subcommand: " << argv[1];
return 1;
}
void InstallSigtermSignalHandler() {
struct sigaction action = {};
action.sa_handler = [](int /*signal*/) {
// Handle SIGTERM gracefully.
// By default, when SIGTERM is received a process will exit with non-zero
// exit code, which will trigger reboot_on_failure handler if one is
// defined. This doesn't play well with userspace reboot which might
// terminate apexd with SIGTERM if apexd was running at the moment of
// userspace reboot, hence this custom handler to exit gracefully.
_exit(0);
};
sigaction(SIGTERM, &action, nullptr);
}
void InstallSelinuxLogging() {
union selinux_callback cb;
cb.func_log = selinux_log_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
}
} // namespace
int main(int argc, char** argv) {
android::base::InitLogging(argv, &android::base::KernelLogger);
// TODO(b/158468454): add a -v flag or an external setting to change severity.
android::base::SetMinimumLogSeverity(android::base::INFO);
const bool has_subcommand = argv[1] != nullptr;
LOG(INFO) << "Started. subcommand = "
<< (has_subcommand ? argv[1] : "(null)");
// set umask to 022 so that files/dirs created are accessible to other
// processes e.g.) /apex/apex-info-list.xml is supposed to be read by other
// processes
umask(022);
// In some scenarios apexd needs to adjust the selinux label of the files.
// Install the selinux logging callback so that we can catch potential errors.
InstallSelinuxLogging();
InstallSigtermSignalHandler();
android::apex::SetConfig(android::apex::kDefaultConfig);
android::apex::ApexdLifecycle& lifecycle =
android::apex::ApexdLifecycle::GetInstance();
bool booting = lifecycle.IsBooting();
if (has_subcommand) {
return HandleSubcommand(argc, argv);
}
// We are running regular apexd, which starts after /metadata/apex/sessions
// and /data/apex/sessions have been created by init. It is safe to create
// ApexSessionManager.
auto session_manager = android::apex::ApexSessionManager::Create(
android::apex::GetSessionsDir());
android::apex::InitializeSessionManager(session_manager.get());
android::base::Result<android::apex::VoldCheckpointInterface>
vold_service_st = android::apex::VoldCheckpointInterface::Create();
android::apex::VoldCheckpointInterface* vold_service = nullptr;
if (!vold_service_st.ok()) {
LOG(ERROR) << "Could not retrieve vold service: "
<< vold_service_st.error();
} else {
vold_service = &*vold_service_st;
}
android::apex::Initialize(vold_service);
if (booting) {
auto res = session_manager->MigrateFromOldSessionsDir(
android::apex::kOldApexSessionsDir);
if (!res.ok()) {
LOG(ERROR) << "Failed to migrate sessions to /metadata partition : "
<< res.error();
}
android::apex::OnStart();
} else {
// TODO(b/172911822): Trying to use data apex related ApexFileRepository
// apis without initializing it should throw error. Also, unit tests should
// not pass without initialization.
// TODO(b/172911822): Consolidate this with Initialize() when
// ApexFileRepository can act as cache and re-scanning is not expensive
android::apex::InitializeDataApex();
}
// start apexservice before ApexdLifecycle::WaitForBootStatus which waits for
// IApexService::markBootComplete().
android::apex::binder::CreateAndRegisterService();
android::apex::binder::StartThreadPool();
if (booting) {
// Notify other components (e.g. init) that all APEXs are correctly mounted
// and activated (but are not yet ready to be used). Configuration based on
// activated APEXs may be performed at this point, but use of APEXs
// themselves should wait for the ready status instead, which is set when
// the "--snapshotde" subcommand is received and snapshot/restore is
// complete.
android::apex::OnAllPackagesActivated(/*is_bootstrap=*/false);
lifecycle.WaitForBootStatus(android::apex::RevertActiveSessionsAndReboot);
// Run cleanup routine on boot complete.
// This should run before AllowServiceShutdown() to prevent
// service_manager killing apexd in the middle of the cleanup.
android::apex::BootCompletedCleanup();
}
android::apex::binder::AllowServiceShutdown();
android::apex::binder::JoinThreadPool();
return 1;
}