/* * 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 #include #include #include #include "apexd.h" #include "apexd_checkpoint_vold.h" #include "apexd_lifecycle.h" #include "apexservice.h" #include #define APEXD_LOG_UNLIMIT #ifdef APEXD_LOG_UNLIMIT #include #include #include using android::base::boot_clock; #include #endif //APEXD_LOG_UNLIMIT namespace { using android::base::SetDefaultTag; int HandleSubcommand(char** argv) { if (strcmp("--bootstrap", argv[1]) == 0) { SetDefaultTag("apexd-bootstrap"); LOG(INFO) << "Bootstrap subcommand detected"; return android::apex::OnBootstrap(); } if (strcmp("--unmount-all", argv[1]) == 0) { SetDefaultTag("apexd-unmount-all"); LOG(INFO) << "Unmount all subcommand detected"; return android::apex::UnmountAll(); } if (strcmp("--otachroot-bootstrap", argv[1]) == 0) { SetDefaultTag("apexd-otachroot"); LOG(INFO) << "OTA chroot bootstrap subcommand detected"; return android::apex::OnOtaChrootBootstrap(); } if (strcmp("--snapshotde", argv[1]) == 0) { SetDefaultTag("apexd-snapshotde"); LOG(INFO) << "Snapshot DE subcommand detected"; // Need to know if checkpointing is enabled so that a prerestore snapshot // can be taken if it's not. android::base::Result 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); } 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"); LOG(INFO) << "VM subcommand detected"; 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); } } // namespace #ifdef APEXD_LOG_UNLIMIT #if defined(__linux__) static int kmsg_fd_ulimit = -1; static uint64_t log_time; static unsigned int log_printed = 0; #define DEFAULT_RATELIMIT_INTERVALMS 5000 #define DEFAULT_RATELIMIT_BURST 9 static android::Mutex kmsg_mutex; static int OpenKmsg_ulimit() { android::Mutex::Autolock _l(kmsg_mutex); uint64_t nowms = std::chrono::duration_cast(boot_clock::now().time_since_epoch()).count(); if (kmsg_fd_ulimit == -1) { kmsg_fd_ulimit = TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC)); log_time = nowms; log_printed = 0; } if (log_printed >= DEFAULT_RATELIMIT_BURST) { int newfd; newfd = TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC)); if (newfd >= 0) { close(kmsg_fd_ulimit); kmsg_fd_ulimit = newfd; log_time = nowms; log_printed = 0; } } if (nowms >= log_time + DEFAULT_RATELIMIT_INTERVALMS) { log_time = nowms; log_printed = 0; } log_printed++; return kmsg_fd_ulimit; } static void KernelLogLine_ulimit(const char* msg, int length, android::base::LogSeverity severity, const char* tag) { // clang-format off static constexpr int kLogSeverityToKernelLogLevel[] = { [android::base::VERBOSE] = 7, // KERN_DEBUG (there is no verbose kernel log // level) [android::base::DEBUG] = 7, // KERN_DEBUG [android::base::INFO] = 6, // KERN_INFO [android::base::WARNING] = 4, // KERN_WARNING [android::base::ERROR] = 3, // KERN_ERROR [android::base::FATAL_WITHOUT_ABORT] = 2, // KERN_CRIT [android::base::FATAL] = 2, // KERN_CRIT }; // clang-format on static_assert(arraysize(kLogSeverityToKernelLogLevel) == android::base::FATAL + 1, "Mismatch in size of kLogSeverityToKernelLogLevel and values in LogSeverity"); int klog_fd = OpenKmsg_ulimit(); if (klog_fd == -1) return; int level = kLogSeverityToKernelLogLevel[severity]; // The kernel's printk buffer is only |1024 - PREFIX_MAX| bytes, where // PREFIX_MAX could be 48 or 32. // Reference: kernel/printk/printk.c static constexpr int LOG_LINE_MAX = 1024 - 48; char buf[LOG_LINE_MAX] __attribute__((__uninitialized__)); size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %.*s\n", level, tag, length, msg); TEMP_FAILURE_RETRY(write(klog_fd, buf, std::min(size, sizeof(buf)))); if (size > sizeof(buf)) { size_t truncated = size - sizeof(buf); size = snprintf( buf, sizeof(buf), "<%d>%s: **previous message missing %zu bytes** %zu-byte message too long for printk\n", level, tag, truncated, size); TEMP_FAILURE_RETRY(write(klog_fd, buf, std::min(size, sizeof(buf)))); } } template static void SplitByLines_ulimit(const char* msg, const F& log_function, Args&&... args) { const char* newline = strchr(msg, '\n'); while (newline != nullptr) { log_function(msg, newline - msg, args...); msg = newline + 1; newline = strchr(msg, '\n'); } log_function(msg, -1, args...); } void KernelLogger_ulimit(android::base::LogId, android::base::LogSeverity severity, const char* tag, const char*, unsigned int, const char* full_message) { SplitByLines_ulimit(full_message, KernelLogLine_ulimit, severity, tag); } #endif //defined(__linux__) #endif //APEXD_LOG_UNLIMIT int main(int /*argc*/, char** argv) { #ifndef APEXD_LOG_UNLIMIT android::base::InitLogging(argv, &android::base::KernelLogger); #else android::base::InitLogging(argv, &KernelLogger_ulimit); #endif //APEXD_LOG_UNLIMIT // TODO(b/158468454): add a -v flag or an external setting to change severity. android::base::SetMinimumLogSeverity(android::base::INFO); // 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); InstallSigtermSignalHandler(); android::apex::SetConfig(android::apex::kDefaultConfig); android::apex::ApexdLifecycle& lifecycle = android::apex::ApexdLifecycle::GetInstance(); bool booting = lifecycle.IsBooting(); const bool has_subcommand = argv[1] != nullptr; if (!android::sysprop::ApexProperties::updatable().value_or(false)) { if (!has_subcommand) { if (!booting) { // We've finished booting, but for some reason somebody tried to start // apexd. Simply exit. return 0; } LOG(INFO) << "This device does not support updatable APEX. Exiting"; // Mark apexd as activated so that init can proceed. android::apex::OnAllPackagesActivated(/*is_bootstrap=*/false); } else if (strcmp("--snapshotde", argv[1]) == 0) { LOG(INFO) << "This device does not support updatable APEX. Exiting"; // mark apexd as ready android::apex::OnAllPackagesReady(); } else if (strcmp("--otachroot-bootstrap", argv[1]) == 0) { SetDefaultTag("apexd-otachroot"); LOG(INFO) << "OTA chroot bootstrap subcommand detected"; return android::apex::ActivateFlattenedApex(); } else if (strcmp("--bootstrap", argv[1]) == 0) { LOG(INFO) << "Bootstrap subcommand detected"; return android::apex::ActivateFlattenedApex(); } return 0; } if (has_subcommand) { return HandleSubcommand(argv); } android::base::Result 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) { if (auto res = android::apex::MigrateSessionsDirIfNeeded(); !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; }