/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { namespace snapshot { using namespace std::chrono_literals; using android::base::unique_fd; bool EnsureSnapuserdStarted() { if (android::base::GetProperty("init.svc.snapuserd", "") == "running") { return true; } android::base::SetProperty("ctl.start", "snapuserd"); if (!android::base::WaitForProperty("init.svc.snapuserd", "running", 10s)) { LOG(ERROR) << "Timed out waiting for snapuserd to start."; return false; } return true; } SnapuserdClient::SnapuserdClient(android::base::unique_fd&& sockfd) : sockfd_(std::move(sockfd)) {} static inline bool IsRetryErrno() { return errno == ECONNREFUSED || errno == EINTR || errno == ENOENT; } std::unique_ptr SnapuserdClient::Connect(const std::string& socket_name, std::chrono::milliseconds timeout_ms) { unique_fd fd; auto start = std::chrono::steady_clock::now(); while (true) { fd.reset(socket_local_client(socket_name.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM)); if (fd >= 0) break; if (fd < 0 && !IsRetryErrno()) { PLOG(ERROR) << "connect failed: " << socket_name; return nullptr; } auto now = std::chrono::steady_clock::now(); auto elapsed = std::chrono::duration_cast(now - start); if (elapsed >= timeout_ms) { LOG(ERROR) << "Timed out connecting to snapuserd socket: " << socket_name; return nullptr; } std::this_thread::sleep_for(100ms); } auto client = std::make_unique(std::move(fd)); if (!client->ValidateConnection()) { return nullptr; } return client; } bool SnapuserdClient::ValidateConnection() { if (!Sendmsg("query")) { return false; } std::string str = Receivemsg(); // If the daemon is passive then fallback to secondary active daemon. Daemon // is passive during transition phase. if (str.find("passive") != std::string::npos) { LOG(ERROR) << "Snapuserd is terminating"; return false; } if (str != "active") { LOG(ERROR) << "Received failure querying daemon"; return false; } return true; } bool SnapuserdClient::Sendmsg(const std::string& msg) { LOG(DEBUG) << "Sendmsg: msg " << msg << " sockfd: " << sockfd_; ssize_t numBytesSent = TEMP_FAILURE_RETRY(send(sockfd_, msg.data(), msg.size(), MSG_NOSIGNAL)); if (numBytesSent < 0) { PLOG(ERROR) << "Send failed"; return false; } if ((size_t)numBytesSent < msg.size()) { LOG(ERROR) << "Partial data sent, expected " << msg.size() << " bytes, sent " << numBytesSent; return false; } return true; } bool SnapuserdClient::WaitForDeviceDelete(const std::string& control_device) { std::string msg = "delete," + control_device; if (!Sendmsg(msg)) { LOG(ERROR) << "Failed to send message " << msg << " to snapuserd"; return false; } std::string response = Receivemsg(); if (response != "success") { LOG(ERROR) << "Failed waiting to delete device " << control_device; return false; } return true; } std::string SnapuserdClient::Receivemsg() { char msg[PACKET_SIZE]; ssize_t ret = TEMP_FAILURE_RETRY(recv(sockfd_, msg, sizeof(msg), 0)); if (ret < 0) { PLOG(ERROR) << "Snapuserd:client: recv failed"; return {}; } if (ret == 0) { LOG(DEBUG) << "Snapuserd:client disconnected"; return {}; } return std::string(msg, ret); } bool SnapuserdClient::StopSnapuserd() { if (!Sendmsg("stop")) { LOG(ERROR) << "Failed to send stop message to snapuserd daemon"; return false; } sockfd_ = {}; return true; } bool SnapuserdClient::AttachDmUser(const std::string& misc_name) { std::string msg = "start," + misc_name; if (!Sendmsg(msg)) { LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon"; return false; } std::string str = Receivemsg(); if (str != "success") { LOG(ERROR) << "Failed to receive ack for " << msg << " from snapuserd daemon"; return false; } LOG(DEBUG) << "Snapuserd daemon initialized with " << msg; return true; } uint64_t SnapuserdClient::InitDmUserCow(const std::string& misc_name, const std::string& cow_device, const std::string& backing_device) { std::vector parts = {"init", misc_name, cow_device, backing_device}; std::string msg = android::base::Join(parts, ","); if (!Sendmsg(msg)) { LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon"; return 0; } std::string str = Receivemsg(); std::vector input = android::base::Split(str, ","); if (input.empty() || input[0] != "success") { LOG(ERROR) << "Failed to receive number of sectors for " << msg << " from snapuserd daemon"; return 0; } LOG(DEBUG) << "Snapuserd daemon COW device initialized: " << cow_device << " Num-sectors: " << input[1]; uint64_t num_sectors = 0; if (!android::base::ParseUint(input[1], &num_sectors)) { LOG(ERROR) << "Failed to parse input string to sectors"; return 0; } return num_sectors; } bool SnapuserdClient::DetachSnapuserd() { if (!Sendmsg("detach")) { LOG(ERROR) << "Failed to detach snapuserd."; return false; } return true; } } // namespace snapshot } // namespace android