blob: 8351155aa99d0f0736d523981220eb60ab249308 [file] [log] [blame]
/*
* 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/logging.h>
#include "snapuserd.h"
#include "snapuserd_server.h"
namespace android {
namespace snapshot {
DaemonOperations SnapuserdServer::Resolveop(std::string& input) {
if (input == "init") return DaemonOperations::INIT;
if (input == "start") return DaemonOperations::START;
if (input == "stop") return DaemonOperations::STOP;
if (input == "query") return DaemonOperations::QUERY;
if (input == "delete") return DaemonOperations::DELETE;
if (input == "detach") return DaemonOperations::DETACH;
return DaemonOperations::INVALID;
}
SnapuserdServer::~SnapuserdServer() {
// 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 SnapuserdServer::GetDaemonStatus() {
std::string msg = "";
if (IsTerminating())
msg = "passive";
else
msg = "active";
return msg;
}
void SnapuserdServer::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 SnapuserdServer::ShutdownThreads() {
StopThreads();
JoinAllThreads();
}
const std::string& DmUserHandler::GetMiscName() const {
return snapuserd_->GetMiscName();
}
bool SnapuserdServer::Sendmsg(android::base::borrowed_fd fd, const std::string& msg) {
ssize_t ret = TEMP_FAILURE_RETRY(send(fd.get(), msg.data(), msg.size(), 0));
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 SnapuserdServer::Recv(android::base::borrowed_fd fd, std::string* data) {
char msg[MAX_PACKET_SIZE];
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 SnapuserdServer::Receivemsg(android::base::borrowed_fd fd, const std::string& str) {
const char delim = ',';
std::vector<std::string> out;
Parsemsg(str, delim, out);
DaemonOperations op = Resolveop(out[0]);
switch (op) {
case DaemonOperations::INIT: {
// Message format:
// init,<misc_name>,<cow_device_path>,<backing_device>
//
// Reads the metadata and send the number of sectors
if (out.size() != 4) {
LOG(ERROR) << "Malformed init message, " << out.size() << " parts";
return Sendmsg(fd, "fail");
}
auto handler = AddHandler(out[1], out[2], out[3]);
if (!handler) {
return Sendmsg(fd, "fail");
}
auto retval = "success," + std::to_string(handler->snapuserd()->GetNumSectors());
return Sendmsg(fd, retval);
}
case DaemonOperations::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()->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 DaemonOperations::STOP: {
// Message format: stop
//
// Stop all the threads gracefully and then shutdown the
// main thread
SetTerminating();
ShutdownThreads();
return true;
}
case DaemonOperations::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 DaemonOperations::DELETE: {
// Message format:
// delete,<misc_name>
if (out.size() != 2) {
LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
return Sendmsg(fd, "fail");
}
if (!RemoveHandler(out[1], true)) {
return Sendmsg(fd, "fail");
}
return Sendmsg(fd, "success");
}
case DaemonOperations::DETACH: {
terminating_ = true;
return Sendmsg(fd, "success");
}
default: {
LOG(ERROR) << "Received unknown message type from client";
Sendmsg(fd, "fail");
return false;
}
}
}
void SnapuserdServer::RunThread(std::shared_ptr<DmUserHandler> handler) {
LOG(INFO) << "Entering thread for handler: " << handler->GetMiscName();
while (!StopRequested()) {
if (!handler->snapuserd()->Run()) {
LOG(INFO) << "Snapuserd: Thread terminating";
break;
}
}
LOG(INFO) << "Exiting thread for handler: " << handler->GetMiscName();
// If the main thread called RemoveHandler, the handler was already removed
// from within the lock, and calling RemoveHandler again has no effect.
RemoveHandler(handler->GetMiscName(), false);
}
bool SnapuserdServer::Start(const std::string& socketname) {
sockfd_.reset(android_get_control_socket(socketname.c_str()));
if (sockfd_ >= 0) {
if (listen(sockfd_.get(), 4) < 0) {
PLOG(ERROR) << "listen socket failed: " << socketname;
return false;
}
} else {
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;
}
}
AddWatchedFd(sockfd_);
LOG(DEBUG) << "Snapuserd server successfully started with socket name " << socketname;
return true;
}
bool SnapuserdServer::Run() {
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 SnapuserdServer::JoinAllThreads() {
// Acquire the thread list within the lock.
std::vector<std::shared_ptr<DmUserHandler>> 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 SnapuserdServer::AddWatchedFd(android::base::borrowed_fd fd) {
struct pollfd p = {};
p.fd = fd.get();
p.events = POLLIN;
watched_fds_.emplace_back(std::move(p));
}
void SnapuserdServer::AcceptClient() {
int fd = TEMP_FAILURE_RETRY(accept4(sockfd_.get(), nullptr, nullptr, SOCK_CLOEXEC));
if (fd < 0) {
PLOG(ERROR) << "accept4 failed";
return;
}
AddWatchedFd(fd);
}
bool SnapuserdServer::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 SnapuserdServer::Interrupt() {
// Force close the socket so poll() fails.
sockfd_ = {};
SetTerminating();
}
std::shared_ptr<DmUserHandler> SnapuserdServer::AddHandler(const std::string& misc_name,
const std::string& cow_device_path,
const std::string& backing_device) {
auto snapuserd = std::make_unique<Snapuserd>(misc_name, cow_device_path, backing_device);
if (!snapuserd->InitCowDevice()) {
LOG(ERROR) << "Failed to initialize Snapuserd";
return nullptr;
}
auto handler = std::make_shared<DmUserHandler>(std::move(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 SnapuserdServer::StartHandler(const std::shared_ptr<DmUserHandler>& handler) {
CHECK(!handler->snapuserd()->IsAttached());
if (!handler->snapuserd()->InitBackingAndControlDevice()) {
LOG(ERROR) << "Failed to initialize control device: " << handler->GetMiscName();
return false;
}
handler->thread() = std::thread(std::bind(&SnapuserdServer::RunThread, this, handler));
return true;
}
auto SnapuserdServer::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)->GetMiscName() == misc_name) {
return iter;
}
}
return dm_users_.end();
}
bool SnapuserdServer::RemoveHandler(const std::string& misc_name, bool wait) {
std::shared_ptr<DmUserHandler> 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() && wait) {
th.join();
} else if (handler->snapuserd()->IsAttached()) {
th.detach();
}
return true;
}
} // namespace snapshot
} // namespace android