summaryrefslogtreecommitdiff
path: root/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp')
-rw-r--r--fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp691
1 files changed, 0 insertions, 691 deletions
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
deleted file mode 100644
index 82b2b25dd..000000000
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ /dev/null
@@ -1,691 +0,0 @@
-/*
- * Copyright (C) 2020 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 <arpa/inet.h>
-#include <cutils/sockets.h>
-#include <errno.h>
-#include <netinet/in.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <android-base/cmsg.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/scopeguard.h>
-#include <fs_mgr/file_wait.h>
-#include <snapuserd/snapuserd_client.h>
-#include "snapuserd_server.h"
-
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
-
-namespace android {
-namespace snapshot {
-
-using namespace std::string_literals;
-
-using android::base::borrowed_fd;
-using android::base::unique_fd;
-
-DaemonOps UserSnapshotServer::Resolveop(std::string& input) {
- if (input == "init") return DaemonOps::INIT;
- if (input == "start") return DaemonOps::START;
- if (input == "stop") return DaemonOps::STOP;
- if (input == "query") return DaemonOps::QUERY;
- if (input == "delete") return DaemonOps::DELETE;
- if (input == "detach") return DaemonOps::DETACH;
- if (input == "supports") return DaemonOps::SUPPORTS;
- if (input == "initiate_merge") return DaemonOps::INITIATE;
- if (input == "merge_percent") return DaemonOps::PERCENTAGE;
- if (input == "getstatus") return DaemonOps::GETSTATUS;
-
- return DaemonOps::INVALID;
-}
-
-UserSnapshotServer::~UserSnapshotServer() {
- // Close any client sockets that were added via AcceptClient().
- for (size_t i = 1; i < watched_fds_.size(); i++) {
- close(watched_fds_[i].fd);
- }
-}
-
-std::string UserSnapshotServer::GetDaemonStatus() {
- std::string msg = "";
-
- if (IsTerminating())
- msg = "passive";
- else
- msg = "active";
-
- return msg;
-}
-
-void UserSnapshotServer::Parsemsg(std::string const& msg, const char delim,
- std::vector<std::string>& out) {
- std::stringstream ss(msg);
- std::string s;
-
- while (std::getline(ss, s, delim)) {
- out.push_back(s);
- }
-}
-
-void UserSnapshotServer::ShutdownThreads() {
- terminating_ = true;
- JoinAllThreads();
-}
-
-UserSnapshotDmUserHandler::UserSnapshotDmUserHandler(std::shared_ptr<SnapshotHandler> snapuserd)
- : snapuserd_(snapuserd), misc_name_(snapuserd_->GetMiscName()) {}
-
-bool UserSnapshotServer::Sendmsg(android::base::borrowed_fd fd, const std::string& msg) {
- ssize_t ret = TEMP_FAILURE_RETRY(send(fd.get(), msg.data(), msg.size(), MSG_NOSIGNAL));
- if (ret < 0) {
- PLOG(ERROR) << "Snapuserd:server: send() failed";
- return false;
- }
-
- if (ret < msg.size()) {
- LOG(ERROR) << "Partial send; expected " << msg.size() << " bytes, sent " << ret;
- return false;
- }
- return true;
-}
-
-bool UserSnapshotServer::Recv(android::base::borrowed_fd fd, std::string* data) {
- char msg[kMaxPacketSize];
- ssize_t rv = TEMP_FAILURE_RETRY(recv(fd.get(), msg, sizeof(msg), 0));
- if (rv < 0) {
- PLOG(ERROR) << "recv failed";
- return false;
- }
- *data = std::string(msg, rv);
- return true;
-}
-
-bool UserSnapshotServer::Receivemsg(android::base::borrowed_fd fd, const std::string& str) {
- const char delim = ',';
-
- std::vector<std::string> out;
- Parsemsg(str, delim, out);
- DaemonOps op = Resolveop(out[0]);
-
- switch (op) {
- case DaemonOps::INIT: {
- // Message format:
- // init,<misc_name>,<cow_device_path>,<backing_device>,<base_path_merge>
- //
- // Reads the metadata and send the number of sectors
- if (out.size() != 5) {
- LOG(ERROR) << "Malformed init message, " << out.size() << " parts";
- return Sendmsg(fd, "fail");
- }
-
- auto handler = AddHandler(out[1], out[2], out[3], out[4]);
- if (!handler) {
- return Sendmsg(fd, "fail");
- }
-
- auto retval = "success," + std::to_string(handler->snapuserd()->GetNumSectors());
- return Sendmsg(fd, retval);
- }
- case DaemonOps::START: {
- // Message format:
- // start,<misc_name>
- //
- // Start the new thread which binds to dm-user misc device
- if (out.size() != 2) {
- LOG(ERROR) << "Malformed start message, " << out.size() << " parts";
- return Sendmsg(fd, "fail");
- }
-
- std::lock_guard<std::mutex> lock(lock_);
- auto iter = FindHandler(&lock, out[1]);
- if (iter == dm_users_.end()) {
- LOG(ERROR) << "Could not find handler: " << out[1];
- return Sendmsg(fd, "fail");
- }
- if (!(*iter)->snapuserd() || (*iter)->snapuserd()->IsAttached()) {
- LOG(ERROR) << "Tried to re-attach control device: " << out[1];
- return Sendmsg(fd, "fail");
- }
- if (!StartHandler(*iter)) {
- return Sendmsg(fd, "fail");
- }
- return Sendmsg(fd, "success");
- }
- case DaemonOps::STOP: {
- // Message format: stop
- //
- // Stop all the threads gracefully and then shutdown the
- // main thread
- SetTerminating();
- ShutdownThreads();
- return true;
- }
- case DaemonOps::QUERY: {
- // Message format: query
- //
- // As part of transition, Second stage daemon will be
- // created before terminating the first stage daemon. Hence,
- // for a brief period client may have to distiguish between
- // first stage daemon and second stage daemon.
- //
- // Second stage daemon is marked as active and hence will
- // be ready to receive control message.
- return Sendmsg(fd, GetDaemonStatus());
- }
- case DaemonOps::DELETE: {
- // Message format:
- // delete,<misc_name>
- if (out.size() != 2) {
- LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
- return Sendmsg(fd, "fail");
- }
- {
- std::lock_guard<std::mutex> lock(lock_);
- auto iter = FindHandler(&lock, out[1]);
- if (iter == dm_users_.end()) {
- // After merge is completed, we swap dm-user table with
- // the underlying dm-linear base device. Hence, worker
- // threads would have terminted and was removed from
- // the list.
- LOG(DEBUG) << "Could not find handler: " << out[1];
- return Sendmsg(fd, "success");
- }
-
- if (!(*iter)->ThreadTerminated()) {
- (*iter)->snapuserd()->NotifyIOTerminated();
- }
- }
- if (!RemoveAndJoinHandler(out[1])) {
- return Sendmsg(fd, "fail");
- }
- return Sendmsg(fd, "success");
- }
- case DaemonOps::DETACH: {
- std::lock_guard<std::mutex> lock(lock_);
- TerminateMergeThreads(&lock);
- terminating_ = true;
- return true;
- }
- case DaemonOps::SUPPORTS: {
- if (out.size() != 2) {
- LOG(ERROR) << "Malformed supports message, " << out.size() << " parts";
- return Sendmsg(fd, "fail");
- }
- if (out[1] == "second_stage_socket_handoff") {
- return Sendmsg(fd, "success");
- }
- return Sendmsg(fd, "fail");
- }
- case DaemonOps::INITIATE: {
- if (out.size() != 2) {
- LOG(ERROR) << "Malformed initiate-merge message, " << out.size() << " parts";
- return Sendmsg(fd, "fail");
- }
- if (out[0] == "initiate_merge") {
- std::lock_guard<std::mutex> lock(lock_);
- auto iter = FindHandler(&lock, out[1]);
- if (iter == dm_users_.end()) {
- LOG(ERROR) << "Could not find handler: " << out[1];
- return Sendmsg(fd, "fail");
- }
-
- if (!StartMerge(*iter)) {
- return Sendmsg(fd, "fail");
- }
-
- return Sendmsg(fd, "success");
- }
- return Sendmsg(fd, "fail");
- }
- case DaemonOps::PERCENTAGE: {
- std::lock_guard<std::mutex> lock(lock_);
- double percentage = GetMergePercentage(&lock);
-
- return Sendmsg(fd, std::to_string(percentage));
- }
- case DaemonOps::GETSTATUS: {
- // Message format:
- // getstatus,<misc_name>
- if (out.size() != 2) {
- LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
- return Sendmsg(fd, "snapshot-merge-failed");
- }
- {
- std::lock_guard<std::mutex> lock(lock_);
- auto iter = FindHandler(&lock, out[1]);
- if (iter == dm_users_.end()) {
- LOG(ERROR) << "Could not find handler: " << out[1];
- return Sendmsg(fd, "snapshot-merge-failed");
- }
-
- std::string merge_status = GetMergeStatus(*iter);
- return Sendmsg(fd, merge_status);
- }
- }
- default: {
- LOG(ERROR) << "Received unknown message type from client";
- Sendmsg(fd, "fail");
- return false;
- }
- }
-}
-
-void UserSnapshotServer::RunThread(std::shared_ptr<UserSnapshotDmUserHandler> handler) {
- LOG(INFO) << "Entering thread for handler: " << handler->misc_name();
-
- if (!handler->snapuserd()->Start()) {
- LOG(ERROR) << " Failed to launch all worker threads";
- }
-
- handler->snapuserd()->CloseFds();
- handler->snapuserd()->CheckMergeCompletionStatus();
- handler->snapuserd()->UnmapBufferRegion();
-
- auto misc_name = handler->misc_name();
- LOG(INFO) << "Handler thread about to exit: " << misc_name;
-
- {
- std::lock_guard<std::mutex> lock(lock_);
- num_partitions_merge_complete_ += 1;
- handler->SetThreadTerminated();
- auto iter = FindHandler(&lock, handler->misc_name());
- if (iter == dm_users_.end()) {
- // RemoveAndJoinHandler() already removed us from the list, and is
- // now waiting on a join(), so just return. Additionally, release
- // all the resources held by snapuserd object which are shared
- // by worker threads. This should be done when the last reference
- // of "handler" is released; but we will explicitly release here
- // to make sure snapuserd object is freed as it is the biggest
- // consumer of memory in the daemon.
- handler->FreeResources();
- LOG(INFO) << "Exiting handler thread to allow for join: " << misc_name;
- return;
- }
-
- LOG(INFO) << "Exiting handler thread and freeing resources: " << misc_name;
-
- if (handler->snapuserd()->IsAttached()) {
- handler->thread().detach();
- }
-
- // Important: free resources within the lock. This ensures that if
- // WaitForDelete() is called, the handler is either in the list, or
- // it's not and its resources are guaranteed to be freed.
- handler->FreeResources();
- dm_users_.erase(iter);
- }
-}
-
-bool UserSnapshotServer::Start(const std::string& socketname) {
- bool start_listening = true;
-
- sockfd_.reset(android_get_control_socket(socketname.c_str()));
- if (sockfd_ < 0) {
- sockfd_.reset(socket_local_server(socketname.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED,
- SOCK_STREAM));
- if (sockfd_ < 0) {
- PLOG(ERROR) << "Failed to create server socket " << socketname;
- return false;
- }
- start_listening = false;
- }
- return StartWithSocket(start_listening);
-}
-
-bool UserSnapshotServer::StartWithSocket(bool start_listening) {
- if (start_listening && listen(sockfd_.get(), 4) < 0) {
- PLOG(ERROR) << "listen socket failed";
- return false;
- }
-
- AddWatchedFd(sockfd_, POLLIN);
- is_socket_present_ = true;
-
- // If started in first-stage init, the property service won't be online.
- if (access("/dev/socket/property_service", F_OK) == 0) {
- if (!android::base::SetProperty("snapuserd.ready", "true")) {
- LOG(ERROR) << "Failed to set snapuserd.ready property";
- return false;
- }
- }
-
- LOG(DEBUG) << "Snapuserd server now accepting connections";
- return true;
-}
-
-bool UserSnapshotServer::Run() {
- LOG(INFO) << "Now listening on snapuserd socket";
-
- while (!IsTerminating()) {
- int rv = TEMP_FAILURE_RETRY(poll(watched_fds_.data(), watched_fds_.size(), -1));
- if (rv < 0) {
- PLOG(ERROR) << "poll failed";
- return false;
- }
- if (!rv) {
- continue;
- }
-
- if (watched_fds_[0].revents) {
- AcceptClient();
- }
-
- auto iter = watched_fds_.begin() + 1;
- while (iter != watched_fds_.end()) {
- if (iter->revents && !HandleClient(iter->fd, iter->revents)) {
- close(iter->fd);
- iter = watched_fds_.erase(iter);
- } else {
- iter++;
- }
- }
- }
-
- JoinAllThreads();
- return true;
-}
-
-void UserSnapshotServer::JoinAllThreads() {
- // Acquire the thread list within the lock.
- std::vector<std::shared_ptr<UserSnapshotDmUserHandler>> dm_users;
- {
- std::lock_guard<std::mutex> guard(lock_);
- dm_users = std::move(dm_users_);
- }
-
- for (auto& client : dm_users) {
- auto& th = client->thread();
-
- if (th.joinable()) th.join();
- }
-}
-
-void UserSnapshotServer::AddWatchedFd(android::base::borrowed_fd fd, int events) {
- struct pollfd p = {};
- p.fd = fd.get();
- p.events = events;
- watched_fds_.emplace_back(std::move(p));
-}
-
-void UserSnapshotServer::AcceptClient() {
- int fd = TEMP_FAILURE_RETRY(accept4(sockfd_.get(), nullptr, nullptr, SOCK_CLOEXEC));
- if (fd < 0) {
- PLOG(ERROR) << "accept4 failed";
- return;
- }
-
- AddWatchedFd(fd, POLLIN);
-}
-
-bool UserSnapshotServer::HandleClient(android::base::borrowed_fd fd, int revents) {
- if (revents & POLLHUP) {
- LOG(DEBUG) << "Snapuserd client disconnected";
- return false;
- }
-
- std::string str;
- if (!Recv(fd, &str)) {
- return false;
- }
- if (!Receivemsg(fd, str)) {
- LOG(ERROR) << "Encountered error handling client message, revents: " << revents;
- return false;
- }
- return true;
-}
-
-void UserSnapshotServer::Interrupt() {
- // Force close the socket so poll() fails.
- sockfd_ = {};
- SetTerminating();
-}
-
-std::shared_ptr<UserSnapshotDmUserHandler> UserSnapshotServer::AddHandler(
- const std::string& misc_name, const std::string& cow_device_path,
- const std::string& backing_device, const std::string& base_path_merge) {
- auto snapuserd = std::make_shared<SnapshotHandler>(misc_name, cow_device_path, backing_device,
- base_path_merge);
- if (!snapuserd->InitCowDevice()) {
- LOG(ERROR) << "Failed to initialize Snapuserd";
- return nullptr;
- }
-
- snapuserd->SetSocketPresent(is_socket_present_);
- snapuserd->SetIouringEnabled(io_uring_enabled_);
-
- if (!snapuserd->InitializeWorkers()) {
- LOG(ERROR) << "Failed to initialize workers";
- return nullptr;
- }
-
- auto handler = std::make_shared<UserSnapshotDmUserHandler>(snapuserd);
- {
- std::lock_guard<std::mutex> lock(lock_);
- if (FindHandler(&lock, misc_name) != dm_users_.end()) {
- LOG(ERROR) << "Handler already exists: " << misc_name;
- return nullptr;
- }
- dm_users_.push_back(handler);
- }
- return handler;
-}
-
-bool UserSnapshotServer::StartHandler(const std::shared_ptr<UserSnapshotDmUserHandler>& handler) {
- if (handler->snapuserd()->IsAttached()) {
- LOG(ERROR) << "Handler already attached";
- return false;
- }
-
- handler->snapuserd()->AttachControlDevice();
-
- handler->thread() = std::thread(std::bind(&UserSnapshotServer::RunThread, this, handler));
- return true;
-}
-
-bool UserSnapshotServer::StartMerge(const std::shared_ptr<UserSnapshotDmUserHandler>& handler) {
- if (!handler->snapuserd()->IsAttached()) {
- LOG(ERROR) << "Handler not attached to dm-user - Merge thread cannot be started";
- return false;
- }
-
- handler->snapuserd()->InitiateMerge();
- return true;
-}
-
-auto UserSnapshotServer::FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
- const std::string& misc_name) -> HandlerList::iterator {
- CHECK(proof_of_lock);
-
- for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
- if ((*iter)->misc_name() == misc_name) {
- return iter;
- }
- }
- return dm_users_.end();
-}
-
-void UserSnapshotServer::TerminateMergeThreads(std::lock_guard<std::mutex>* proof_of_lock) {
- CHECK(proof_of_lock);
-
- for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
- if (!(*iter)->ThreadTerminated()) {
- (*iter)->snapuserd()->NotifyIOTerminated();
- }
- }
-}
-
-std::string UserSnapshotServer::GetMergeStatus(
- const std::shared_ptr<UserSnapshotDmUserHandler>& handler) {
- return handler->snapuserd()->GetMergeStatus();
-}
-
-double UserSnapshotServer::GetMergePercentage(std::lock_guard<std::mutex>* proof_of_lock) {
- CHECK(proof_of_lock);
- double percentage = 0.0;
- int n = 0;
-
- for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
- auto& th = (*iter)->thread();
- if (th.joinable()) {
- // Merge percentage by individual partitions wherein merge is still
- // in-progress
- percentage += (*iter)->snapuserd()->GetMergePercentage();
- n += 1;
- }
- }
-
- // Calculate final merge including those partitions where merge was already
- // completed - num_partitions_merge_complete_ will track them when each
- // thread exists in RunThread.
- int total_partitions = n + num_partitions_merge_complete_;
-
- if (total_partitions) {
- percentage = ((num_partitions_merge_complete_ * 100.0) + percentage) / total_partitions;
- }
-
- LOG(DEBUG) << "Merge %: " << percentage
- << " num_partitions_merge_complete_: " << num_partitions_merge_complete_
- << " total_partitions: " << total_partitions << " n: " << n;
- return percentage;
-}
-
-bool UserSnapshotServer::RemoveAndJoinHandler(const std::string& misc_name) {
- std::shared_ptr<UserSnapshotDmUserHandler> handler;
- {
- std::lock_guard<std::mutex> lock(lock_);
-
- auto iter = FindHandler(&lock, misc_name);
- if (iter == dm_users_.end()) {
- // Client already deleted.
- return true;
- }
- handler = std::move(*iter);
- dm_users_.erase(iter);
- }
-
- auto& th = handler->thread();
- if (th.joinable()) {
- th.join();
- }
- return true;
-}
-
-bool UserSnapshotServer::WaitForSocket() {
- auto scope_guard = android::base::make_scope_guard([this]() -> void { JoinAllThreads(); });
-
- auto socket_path = ANDROID_SOCKET_DIR "/"s + kSnapuserdSocketProxy;
-
- if (!android::fs_mgr::WaitForFile(socket_path, std::chrono::milliseconds::max())) {
- LOG(ERROR)
- << "Failed to wait for proxy socket, second-stage snapuserd will fail to connect";
- return false;
- }
-
- // This initialization of system property is important. When daemon is
- // launched post selinux transition (before init second stage),
- // bionic libc initializes system property as part of __libc_init_common();
- // however that initialization fails silently given that fact that we don't
- // have /dev/__properties__ setup which is created at init second stage.
- //
- // At this point, we have the handlers setup and is safe to setup property.
- __system_properties_init();
-
- if (!android::base::WaitForProperty("snapuserd.proxy_ready", "true")) {
- LOG(ERROR)
- << "Failed to wait for proxy property, second-stage snapuserd will fail to connect";
- return false;
- }
-
- unique_fd fd(socket_local_client(kSnapuserdSocketProxy, ANDROID_SOCKET_NAMESPACE_RESERVED,
- SOCK_SEQPACKET));
- if (fd < 0) {
- PLOG(ERROR) << "Failed to connect to socket proxy";
- return false;
- }
-
- char code[1];
- std::vector<unique_fd> fds;
- ssize_t rv = android::base::ReceiveFileDescriptorVector(fd, code, sizeof(code), 1, &fds);
- if (rv < 0) {
- PLOG(ERROR) << "Failed to receive server socket over proxy";
- return false;
- }
- if (fds.empty()) {
- LOG(ERROR) << "Expected at least one file descriptor from proxy";
- return false;
- }
-
- // We don't care if the ACK is received.
- code[0] = 'a';
- if (TEMP_FAILURE_RETRY(send(fd, code, sizeof(code), MSG_NOSIGNAL) < 0)) {
- PLOG(ERROR) << "Failed to send ACK to proxy";
- return false;
- }
-
- sockfd_ = std::move(fds[0]);
- if (!StartWithSocket(true)) {
- return false;
- }
- return Run();
-}
-
-bool UserSnapshotServer::RunForSocketHandoff() {
- unique_fd proxy_fd(android_get_control_socket(kSnapuserdSocketProxy));
- if (proxy_fd < 0) {
- PLOG(FATAL) << "Proxy could not get android control socket " << kSnapuserdSocketProxy;
- }
- borrowed_fd server_fd(android_get_control_socket(kSnapuserdSocket));
- if (server_fd < 0) {
- PLOG(FATAL) << "Proxy could not get android control socket " << kSnapuserdSocket;
- }
-
- if (listen(proxy_fd.get(), 4) < 0) {
- PLOG(FATAL) << "Proxy listen socket failed";
- }
-
- if (!android::base::SetProperty("snapuserd.proxy_ready", "true")) {
- LOG(FATAL) << "Proxy failed to set ready property";
- }
-
- unique_fd client_fd(
- TEMP_FAILURE_RETRY(accept4(proxy_fd.get(), nullptr, nullptr, SOCK_CLOEXEC)));
- if (client_fd < 0) {
- PLOG(FATAL) << "Proxy accept failed";
- }
-
- char code[1] = {'a'};
- std::vector<int> fds = {server_fd.get()};
- ssize_t rv = android::base::SendFileDescriptorVector(client_fd, code, sizeof(code), fds);
- if (rv < 0) {
- PLOG(FATAL) << "Proxy could not send file descriptor to snapuserd";
- }
- // Wait for an ACK - results don't matter, we just don't want to risk closing
- // the proxy socket too early.
- if (recv(client_fd, code, sizeof(code), 0) < 0) {
- PLOG(FATAL) << "Proxy could not receive terminating code from snapuserd";
- }
- return true;
-}
-
-} // namespace snapshot
-} // namespace android