blob: 38988cef168c686878f4e48bc27bd6d4044b6f95 [file]
/*
* Copyright (C) 2018 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 <algorithm>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef __APPLE__
#include <mach-o/dyld.h>
#endif
#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/log.h"
#include "tools/base/deploy/common/utils.h"
#include "tools/base/deploy/installer/command_cmd.h"
#include "tools/base/deploy/installer/dump.h"
#include "tools/base/deploy/installer/executor/executor_impl.h"
#include "tools/base/deploy/installer/executor/redirect_executor.h"
#include "tools/base/deploy/installer/workspace.h"
#include "tools/base/deploy/proto/deploy.pb.h"
using namespace deploy;
struct Parameters {
const char* binary_name = nullptr;
const char* command_name = nullptr;
const char* cmd_path = nullptr;
const char* pm_path = nullptr;
const char* version = nullptr;
int consumed = 0;
};
std::string GetStringUsage(const char* invoked_path) {
std::stringstream buffer;
buffer << "Usage:" << std::endl
<< invoked_path << " [env parameters] command [command_parameters]"
<< std::endl
<< std::endl
<< "Environment parameters available:" << std::endl
<< " -cmd=X: Define path to cmd executable (to mock android)."
<< std::endl
<< " -pm=X : Define path to package manager executable (to mock "
"android)."
<< std::endl
<< " -version=X : Program will fail if version != X." << std::endl
<< "Commands available:" << std::endl
<< " dump : Extract CDs and Signatures for a given applicationID."
<< std::endl
<< " swap : Perform a hot-swap via JVMTI." << std::endl
<< std::endl;
return buffer.str();
}
bool ParseParameters(int argc, char** argv, Parameters* parameters) {
parameters->binary_name = argv[0];
parameters->consumed = 1;
int index = 1;
while (index < argc && argv[index][0] == '-') {
strtok(argv[index], "=");
if (!strncmp("-cmd", argv[index], 4)) {
parameters->cmd_path = strtok(nullptr, "=");
} else if (!strncmp("-pm", argv[index], 3)) {
parameters->pm_path = strtok(nullptr, "=");
} else if (!strncmp("-version", argv[index], 8)) {
parameters->version = strtok(nullptr, "=");
} else {
std::cerr << "environment parameter unknown:" << argv[index] << std::endl;
return false;
}
parameters->consumed++;
index++;
}
if (index < argc) {
parameters->command_name = argv[index];
parameters->consumed++;
}
return true;
}
void SendResponse(proto::InstallerResponse* response,
const Workspace& workspace) noexcept {
std::unique_ptr<std::vector<Event>> events = ConsumeEvents();
for (Event& event : *events) {
ConvertEventToProtoEvent(event, response->add_events());
}
std::string response_string;
response->SerializeToString(&response_string);
workspace.GetOutput().Write(response_string);
}
int Fail(proto::InstallerResponse::Status status, Workspace& workspace,
const std::string& message) {
proto::InstallerResponse response;
response.set_status(status);
ErrEvent(message);
SendResponse(&response, workspace);
return EXIT_FAILURE;
}
std::string GetVersion() {
static std::string version = "";
if (!version.empty()) {
return version;
}
matryoshka::Doll* doll = matryoshka::OpenByName("version");
if (doll) {
version = std::string((char*)doll->content, doll->content_len);
delete doll;
return version;
} else {
return "UNVERSIONED";
}
}
int main(int argc, char** argv) {
InitEventSystem();
BeginPhase("installer");
ExecutorImpl executor;
Workspace workspace(GetVersion(), &executor);
// Check and parse parameters
if (argc < 2) {
std::string message = GetStringUsage(argv[0]);
return Fail(proto::InstallerResponse::ERROR_PARAMETER, workspace, message);
}
Parameters parameters;
bool parametersParsed = ParseParameters(argc, argv, &parameters);
if (!parametersParsed) {
std::string message = GetStringUsage(argv[0]);
return Fail(proto::InstallerResponse::ERROR_PARAMETER, workspace, message);
}
if (parameters.cmd_path != nullptr) {
workspace.SetCmdPath(parameters.cmd_path);
}
if (parameters.pm_path != nullptr) {
workspace.SetPmPath(parameters.pm_path);
}
RedirectExecutor redirect(Env::shell(), executor);
if (Env::IsValid()) {
workspace.SetExecutor(&redirect);
}
workspace.Init();
// Verify that this program is the version the called expected.
if (parameters.version != nullptr &&
strcmp(parameters.version, GetVersion().c_str())) {
std::string message = "Version mismatch. Requested:"_s +
parameters.version + " but have " + GetVersion();
return Fail(proto::InstallerResponse::ERROR_WRONG_VERSION, workspace,
message);
}
// Retrieve Command to be invoked.
auto task = GetCommand(parameters.command_name, workspace);
if (task == nullptr) {
return Fail(proto::InstallerResponse::ERROR_CMD, workspace,
"Unknown command");
}
// Allow command to parse its parameters and invoke it.
task->ParseParameters(argc - parameters.consumed, argv + parameters.consumed);
if (!task->ReadyToRun()) {
std::string message =
"Command "_s + parameters.command_name + ": wrong parameters";
return Fail(proto::InstallerResponse::ERROR_PARAMETER, workspace, message);
}
// Finally! Run !
proto::InstallerResponse response;
task->Run(&response);
response.set_status(proto::InstallerResponse::OK);
EndPhase();
SendResponse(&response, workspace);
return EXIT_SUCCESS;
}