blob: b4c5d23982fba2728c02cc7f8974d84192e21589 [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/overlay_install.h"
#include <string>
#include "tools/base/deploy/common/event.h"
#include "tools/base/deploy/common/message_pipe_wrapper.h"
#include "tools/base/deploy/common/utils.h"
#include "tools/base/deploy/installer/binary_extract.h"
#include "tools/base/deploy/installer/executor/executor.h"
#include "tools/base/deploy/installer/executor/runas_executor.h"
#include "tools/base/deploy/installer/server/app_servers.h"
#include "tools/base/deploy/sites/sites.h"
namespace deploy {
void OverlayInstallCommand::ParseParameters(
const proto::InstallerRequest& request) {
if (!request.has_overlay_install()) {
return;
}
request_ = request.overlay_install();
ready_to_run_ = true;
}
void OverlayInstallCommand::Run(proto::InstallerResponse* response) {
proto::OverlayInstallResponse* overlay_response =
response->mutable_overlay_install_response();
// Determine which agent we need to use.
#if defined(__aarch64__) || defined(__x86_64__)
std::string agent =
request_.arch() == proto::Arch::ARCH_64_BIT ? kAgent : kAgentAlt;
#else
std::string agent = kAgent;
#endif
if (!ExtractBinaries(workspace_.GetTmpFolder(), {agent, kInstallServer})) {
overlay_response->set_status(proto::OverlayInstallResponse::SETUP_FAILED);
ErrEvent("Extracting binaries failed");
return;
}
client_ = AppServers::Get(request_.package_name(), workspace_.GetTmpFolder(),
workspace_.GetVersion());
if (!SetUpAgent(agent, overlay_response)) {
overlay_response->set_status(proto::OverlayInstallResponse::SETUP_FAILED);
ErrEvent("OverlayInstall: SetupAgent failed");
return;
}
UpdateOverlay(overlay_response);
GetAgentLogs(overlay_response);
}
bool OverlayInstallCommand::SetUpAgent(
const std::string& agent, proto::OverlayInstallResponse* overlay_response) {
Phase p("SetUpAgent");
std::string version = workspace_.GetVersion() + "-";
std::string startup_path = Sites::AppStartupAgent(request_.package_name());
std::string studio_path = Sites::AppStudio(request_.package_name());
std::string agent_path = startup_path + version + agent;
std::unordered_set<std::string> missing_files;
if (!CheckFilesExist({startup_path, studio_path, agent_path},
&missing_files)) {
ErrEvent("LiveLiteral: CheckFilesExist failed");
return false;
}
RunasExecutor run_as(request_.package_name());
std::string error;
bool missing_startup =
missing_files.find(startup_path) != missing_files.end();
bool missing_agent = missing_files.find(agent_path) != missing_files.end();
// Clean up other agents from the startup_agent directory. Because agents are
// versioned (agent-<version#>) we cannot simply copy our agent on top of the
// previous file. If the startup_agent directory exists but our agent cannot
// be found in it, we assume another agent is present and delete it.
if (!missing_startup && missing_agent) {
if (!run_as.Run("rm", {"-f", "-r", startup_path}, nullptr, &error)) {
ErrEvent("Could not remove old agents: " + error);
overlay_response->set_status(proto::OverlayInstallResponse::SETUP_FAILED);
return false;
}
missing_startup = true;
}
if (missing_startup &&
!run_as.Run("mkdir", {startup_path}, nullptr, &error)) {
ErrEvent("Could not create startup agent directory: " + error);
overlay_response->set_status(proto::OverlayInstallResponse::SETUP_FAILED);
return false;
}
if (missing_files.find(studio_path) != missing_files.end() &&
!run_as.Run("mkdir", {studio_path}, nullptr, &error)) {
ErrEvent("Could not create studio directory: " + error);
overlay_response->set_status(proto::OverlayInstallResponse::SETUP_FAILED);
return false;
}
std::string tmp_agent = workspace_.GetTmpFolder() + agent;
if (missing_agent &&
!run_as.Run("cp", {"-F", tmp_agent, agent_path}, nullptr, &error)) {
ErrEvent("Could not copy binaries: " + error);
overlay_response->set_status(proto::OverlayInstallResponse::SETUP_FAILED);
return false;
}
return true;
}
void OverlayInstallCommand::UpdateOverlay(
proto::OverlayInstallResponse* overlay_response) {
Phase p("UpdateOverlay");
proto::OverlayUpdateRequest overlay_request;
overlay_request.set_overlay_id(request_.overlay_id());
overlay_request.set_expected_overlay_id(request_.expected_overlay_id());
const std::string overlay_path = Sites::AppOverlays(request_.package_name());
overlay_request.set_overlay_path(overlay_path);
overlay_request.set_package_name(request_.package_name());
for (auto overlay_file : request_.overlay_files()) {
auto file = overlay_request.add_files_to_write();
file->set_path(overlay_file.path());
file->set_allocated_content(overlay_file.release_content());
}
for (auto deleted_file : request_.deleted_files()) {
overlay_request.add_files_to_delete(deleted_file);
}
auto resp = client_->UpdateOverlay(overlay_request);
if (!resp) {
ErrEvent("Could send update to install server");
overlay_response->set_status(
proto::OverlayInstallResponse::INSTALL_SERVER_COM_ERR);
return;
}
switch (resp->status()) {
case proto::OverlayUpdateResponse::OK:
overlay_response->set_status(proto::OverlayInstallResponse::OK);
return;
case proto::OverlayUpdateResponse::ID_MISMATCH:
overlay_response->set_status(
proto::OverlayInstallResponse::OVERLAY_ID_MISMATCH);
overlay_response->set_extra(resp->error_message());
return;
case proto::OverlayUpdateResponse::UPDATE_FAILED:
overlay_response->set_status(
proto::OverlayInstallResponse::OVERLAY_UPDATE_FAILED);
overlay_response->set_extra(resp->error_message());
return;
}
}
bool OverlayInstallCommand::CheckFilesExist(
const std::vector<std::string>& files,
std::unordered_set<std::string>* missing_files) {
Phase p("CheckFilesExist");
proto::CheckSetupRequest request;
for (const std::string& file : files) {
request.add_files(file);
}
auto resp = client_->CheckSetup(request);
if (!resp) {
return false;
}
missing_files->insert(resp->missing_files().begin(),
resp->missing_files().end());
return true;
}
void OverlayInstallCommand::GetAgentLogs(
proto::OverlayInstallResponse* response) {
Phase p("GetAgentLogs");
proto::GetAgentExceptionLogRequest req;
req.set_package_name(request_.package_name());
// If this fails, we don't really care - it's a best-effort situation; don't
// break the deployment because of it. Just log and move on.
auto resp = client_->GetAgentExceptionLog(req);
if (resp == nullptr) {
return;
}
for (const auto& log : resp->logs()) {
auto added = response->add_agent_logs();
*added = log;
}
}
} // namespace deploy