| /* |
| * 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, ¶meters); |
| 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; |
| } |