blob: 9c241114dc43532fa8b87b6ce29d9d560c536631 [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 "tools/base/deploy/installer/base_swap.h"
#include <fcntl.h>
#include <sys/wait.h>
#include "tools/base/bazel/native/matryoshka/doll.h"
#include "tools/base/deploy/common/env.h"
#include "tools/base/deploy/common/event.h"
#include "tools/base/deploy/common/message_pipe_wrapper.h"
#include "tools/base/deploy/common/socket.h"
#include "tools/base/deploy/common/utils.h"
#include "tools/base/deploy/installer/binary_extract.h"
#include "tools/base/deploy/installer/command_cmd.h"
#include "tools/base/deploy/installer/executor/runas_executor.h"
#include "tools/base/deploy/installer/server/app_servers.h"
namespace {
// These values are based on FIRST_APPLICATION_UID and LAST_APPLICATION_UID in
// android.os.Process, which we assume are stable since they haven't been
// changed since 2012.
const int kFirstAppUid = 10000;
const int kLastAppUid = 19999;
} // namespace
namespace deploy {
void BaseSwapCommand::Run(proto::InstallerResponse* response) {
proto::SwapResponse* swap_response = response->mutable_swap_response();
if (!ExtractBinaries(workspace_.GetTmpFolder(),
{kAgent, kAgentAlt, kInstallServer})) {
swap_response->set_status(proto::SwapResponse::SETUP_FAILED);
ErrEvent("Extracting binaries failed");
return;
}
client_ = AppServers::Get(package_name_, workspace_.GetTmpFolder(),
workspace_.GetVersion());
std::unique_ptr<proto::SwapRequest> request = PrepareAndBuildRequest();
if (request == nullptr) {
swap_response->set_status(proto::SwapResponse::SETUP_FAILED);
ErrEvent("BaseSwapCommand: Unable to PrepareAndBuildRequest");
return;
}
Swap(std::move(request), swap_response);
ProcessResponse(swap_response);
}
bool BaseSwapCommand::Swap(
const std::unique_ptr<proto::SwapRequest> swap_request,
proto::SwapResponse* swap_response) {
Phase p("Swap");
if (swap_response->status() != proto::SwapResponse::UNKNOWN) {
ErrEvent(
"BaseSwapCommand: Unable to Swap (swapResponse status is populated)");
return false;
}
// Remove process ids that we do not need to swap.
FilterProcessIds(&process_ids_);
// Don't bother with the server if we have no work to do.
if (process_ids_.empty() && extra_agents_count_ == 0) {
LogEvent("No PIDs needs to be swapped");
swap_response->set_status(proto::SwapResponse::OK);
return true;
}
// Request for the install-server to open a socket and begin listening for
// agents to connect. Agents connect shortly after they are attached (below).
proto::SwapResponse::Status status = ListenForAgents();
if (status != proto::SwapResponse::OK) {
swap_response->set_status(status);
return false;
}
if (!Attach(process_ids_, agent_path_)) {
swap_response->set_status(proto::SwapResponse::AGENT_ATTACH_FAILED);
return false;
}
// Request for the install-server to accept a connection for each agent
// attached. The install-server will forward the specified swap request to
// every agent, then return an aggregate list of each agent's response.
// TODO: Move this block to its own function.
proto::SendAgentMessageRequest req;
req.set_agent_count(process_ids_.size() + extra_agents_count_);
*req.mutable_agent_request()->mutable_swap_request() = *swap_request.get();
auto resp = client_->SendAgentMessage(req);
if (!resp) {
swap_response->set_status(proto::SwapResponse::INSTALL_SERVER_COM_ERR);
return false;
}
for (const auto& agent_response : resp->agent_responses()) {
ConvertProtoEventsToEvents(agent_response.events());
if (agent_response.status() != proto::AgentResponse::OK) {
auto failed_agent = swap_response->add_failed_agents();
*failed_agent = agent_response;
}
}
if (resp->status() == proto::SendAgentMessageResponse::OK) {
if (swap_response->failed_agents_size() == 0) {
swap_response->set_status(proto::SwapResponse::OK);
return true;
} else {
swap_response->set_status(proto::SwapResponse::AGENT_ERROR);
return false;
}
}
CmdCommand cmd(workspace_);
std::vector<ProcessRecord> records;
if (cmd.GetProcessInfo(package_name_, &records)) {
for (auto& record : records) {
if (record.crashing) {
swap_response->set_status(proto::SwapResponse::PROCESS_CRASHING);
swap_response->set_extra(record.process_name);
return false;
}
if (record.not_responding) {
swap_response->set_status(proto::SwapResponse::PROCESS_NOT_RESPONDING);
swap_response->set_extra(record.process_name);
return false;
}
}
}
for (int pid : swap_request->process_ids()) {
const std::string pid_string = to_string(pid);
if (IO::access("/proc/" + pid_string, F_OK) != 0) {
swap_response->set_status(proto::SwapResponse::PROCESS_TERMINATED);
swap_response->set_extra(pid_string);
return false;
}
}
swap_response->set_status(proto::SwapResponse::MISSING_AGENT_RESPONSES);
return false;
}
void BaseSwapCommand::FilterProcessIds(std::vector<int>* process_ids) {
Phase p("FilterProcessIds");
auto it = process_ids->begin();
while (it != process_ids->end()) {
const int pid = *it;
const std::string pid_path = "/proc/" + to_string(pid);
struct stat proc_dir_stat;
if (IO::stat(pid_path, &proc_dir_stat) < 0) {
LogEvent("Ignoring pid '" + to_string(pid) + "'; could not stat().");
it = process_ids->erase(it);
} else if (proc_dir_stat.st_uid < kFirstAppUid ||
proc_dir_stat.st_uid > kLastAppUid) {
LogEvent("Ignoring pid '" + to_string(pid) +
"'; uid=" + to_string(proc_dir_stat.st_uid) +
" is not in the app uid range.");
it = process_ids->erase(it);
} else {
++it;
}
}
}
proto::SwapResponse::Status BaseSwapCommand::ListenForAgents() {
Phase("ListenForAgents");
proto::OpenAgentSocketRequest req;
req.set_socket_name(GetSocketName());
auto resp = client_->OpenAgentSocket(req);
if (!resp) {
return proto::SwapResponse::INSTALL_SERVER_COM_ERR;
}
if (resp->status() != proto::OpenAgentSocketResponse::OK) {
return proto::SwapResponse::READY_FOR_AGENTS_NOT_RECEIVED;
}
return proto::SwapResponse::OK;
}
bool BaseSwapCommand::CheckFilesExist(
const std::vector<std::string>& files,
std::unordered_set<std::string>* missing_files) {
proto::CheckSetupRequest req;
for (const std::string& file : files) {
req.add_files(file);
}
auto resp = client_->CheckSetup(req);
if (!resp) {
return false;
}
missing_files->insert(resp->missing_files().begin(),
resp->missing_files().end());
return true;
}
} // namespace deploy