// // Copyright (C) 2022 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 "host/commands/cvd/command_sequence.h" #include #include "common/libs/fs/shared_buf.h" #include "host/commands/cvd/server.h" #include "host/commands/cvd/server_client.h" #include "host/commands/cvd/types.h" namespace cuttlefish { namespace { std::string BashEscape(const std::string& input) { bool safe = true; for (const auto& c : input) { if ('0' <= c && c <= '9') { continue; } if ('a' <= c && c <= 'z') { continue; } if ('A' <= c && c <= 'Z') { continue; } if (c == '_' || c == '-' || c == '.' || c == ',' || c == '/') { continue; } safe = false; } using android::base::StringReplace; return safe ? input : "'" + StringReplace(input, "'", "\\'", true) + "'"; } std::string FormattedCommand(const cvd::CommandRequest command) { std::stringstream effective_command; effective_command << "Executing `"; for (const auto& [name, val] : command.env()) { effective_command << BashEscape(name) << "=" << BashEscape(val) << " "; } auto args = cvd_common::ConvertToArgs(command.args()); auto selector_args = cvd_common::ConvertToArgs(command.selector_opts().args()); if (args.empty()) { return effective_command.str(); } const auto& cmd = args.front(); cvd_common::Args cmd_args{args.begin() + 1, args.end()}; effective_command << BashEscape(cmd) << " "; for (const auto& selector_arg : selector_args) { effective_command << BashEscape(selector_arg) << " "; } for (const auto& cmd_arg : cmd_args) { effective_command << BashEscape(cmd_arg) << " "; } effective_command.seekp(-1, effective_command.cur); effective_command << "`\n"; // Overwrite last space return effective_command.str(); } } // namespace CommandSequenceExecutor::CommandSequenceExecutor() {} Result CommandSequenceExecutor::LateInject(fruit::Injector<>& injector) { server_handlers_ = injector.getMultibindings(); return {}; } Result CommandSequenceExecutor::Interrupt() { std::unique_lock interrupt_lock(interrupt_mutex_); interrupted_ = true; if (handler_stack_.empty()) { return {}; } CF_EXPECT(handler_stack_.back()->Interrupt()); return {}; } Result> CommandSequenceExecutor::Execute( const std::vector& requests, SharedFD report) { std::unique_lock interrupt_lock(interrupt_mutex_); CF_EXPECT(!interrupted_, "Interrupted"); std::vector responses; for (const auto& request : requests) { auto& inner_proto = request.Message(); if (inner_proto.has_command_request()) { auto& command = inner_proto.command_request(); std::string str = FormattedCommand(command); CF_EXPECT(WriteAll(report, str) == str.size(), report->StrError()); } auto handler = CF_EXPECT(RequestHandler(request, server_handlers_)); handler_stack_.push_back(handler); interrupt_lock.unlock(); auto response = CF_EXPECT(handler->Handle(request)); interrupt_lock.lock(); handler_stack_.pop_back(); CF_EXPECT(interrupted_ == false, "Interrupted"); CF_EXPECT(response.status().code() == cvd::Status::OK, "Reason: \"" << response.status().message() << "\""); responses.emplace_back(std::move(response)); } return {responses}; } Result CommandSequenceExecutor::ExecuteOne( const RequestWithStdio& request, SharedFD report) { auto response_in_vector = CF_EXPECT(Execute({request}, report)); CF_EXPECT_EQ(response_in_vector.size(), 1); return response_in_vector.front(); } std::vector CommandSequenceExecutor::CmdList() const { std::unordered_set subcmds; for (const auto& handler : server_handlers_) { auto&& cmds_list = handler->CmdList(); for (const auto& cmd : cmds_list) { subcmds.insert(cmd); } } // duplication removed return std::vector{subcmds.begin(), subcmds.end()}; } fruit::Component CommandSequenceExecutorComponent() { return fruit::createComponent() .addMultibinding(); } } // namespace cuttlefish