• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2022 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
16 #include "host/commands/cvd/command_sequence.h"
17 
18 #include <fruit/fruit.h>
19 
20 #include "common/libs/fs/shared_buf.h"
21 #include "host/commands/cvd/server.h"
22 #include "host/commands/cvd/server_client.h"
23 #include "host/commands/cvd/types.h"
24 
25 namespace cuttlefish {
26 namespace {
27 
BashEscape(const std::string & input)28 std::string BashEscape(const std::string& input) {
29   bool safe = true;
30   for (const auto& c : input) {
31     if ('0' <= c && c <= '9') {
32       continue;
33     }
34     if ('a' <= c && c <= 'z') {
35       continue;
36     }
37     if ('A' <= c && c <= 'Z') {
38       continue;
39     }
40     if (c == '_' || c == '-' || c == '.' || c == ',' || c == '/') {
41       continue;
42     }
43     safe = false;
44   }
45   using android::base::StringReplace;
46   return safe ? input : "'" + StringReplace(input, "'", "\\'", true) + "'";
47 }
48 
FormattedCommand(const cvd::CommandRequest command)49 std::string FormattedCommand(const cvd::CommandRequest command) {
50   std::stringstream effective_command;
51   effective_command << "Executing `";
52   for (const auto& [name, val] : command.env()) {
53     effective_command << BashEscape(name) << "=" << BashEscape(val) << " ";
54   }
55   auto args = cvd_common::ConvertToArgs(command.args());
56   auto selector_args =
57       cvd_common::ConvertToArgs(command.selector_opts().args());
58   if (args.empty()) {
59     return effective_command.str();
60   }
61   const auto& cmd = args.front();
62   cvd_common::Args cmd_args{args.begin() + 1, args.end()};
63   effective_command << BashEscape(cmd) << " ";
64   for (const auto& selector_arg : selector_args) {
65     effective_command << BashEscape(selector_arg) << " ";
66   }
67   for (const auto& cmd_arg : cmd_args) {
68     effective_command << BashEscape(cmd_arg) << " ";
69   }
70   effective_command.seekp(-1, effective_command.cur);
71   effective_command << "`\n";  // Overwrite last space
72   return effective_command.str();
73 }
74 
75 }  // namespace
76 
CommandSequenceExecutor()77 CommandSequenceExecutor::CommandSequenceExecutor() {}
78 
LateInject(fruit::Injector<> & injector)79 Result<void> CommandSequenceExecutor::LateInject(fruit::Injector<>& injector) {
80   server_handlers_ = injector.getMultibindings<CvdServerHandler>();
81   return {};
82 }
83 
Interrupt()84 Result<void> CommandSequenceExecutor::Interrupt() {
85   std::unique_lock interrupt_lock(interrupt_mutex_);
86   interrupted_ = true;
87   if (handler_stack_.empty()) {
88     return {};
89   }
90   CF_EXPECT(handler_stack_.back()->Interrupt());
91   return {};
92 }
93 
Execute(const std::vector<RequestWithStdio> & requests,SharedFD report)94 Result<std::vector<cvd::Response>> CommandSequenceExecutor::Execute(
95     const std::vector<RequestWithStdio>& requests, SharedFD report) {
96   std::unique_lock interrupt_lock(interrupt_mutex_);
97   CF_EXPECT(!interrupted_, "Interrupted");
98 
99   std::vector<cvd::Response> responses;
100   for (const auto& request : requests) {
101     auto& inner_proto = request.Message();
102     if (inner_proto.has_command_request()) {
103       auto& command = inner_proto.command_request();
104       std::string str = FormattedCommand(command);
105       CF_EXPECT(WriteAll(report, str) == str.size(), report->StrError());
106     }
107 
108     auto handler = CF_EXPECT(RequestHandler(request, server_handlers_));
109     handler_stack_.push_back(handler);
110     interrupt_lock.unlock();
111     auto response = CF_EXPECT(handler->Handle(request));
112     interrupt_lock.lock();
113     handler_stack_.pop_back();
114 
115     CF_EXPECT(interrupted_ == false, "Interrupted");
116     CF_EXPECT(response.status().code() == cvd::Status::OK,
117               "Reason: \"" << response.status().message() << "\"");
118 
119     responses.emplace_back(std::move(response));
120   }
121   return {responses};
122 }
123 
ExecuteOne(const RequestWithStdio & request,SharedFD report)124 Result<cvd::Response> CommandSequenceExecutor::ExecuteOne(
125     const RequestWithStdio& request, SharedFD report) {
126   auto response_in_vector = CF_EXPECT(Execute({request}, report));
127   CF_EXPECT_EQ(response_in_vector.size(), 1);
128   return response_in_vector.front();
129 }
130 
CmdList() const131 std::vector<std::string> CommandSequenceExecutor::CmdList() const {
132   std::unordered_set<std::string> subcmds;
133   for (const auto& handler : server_handlers_) {
134     auto&& cmds_list = handler->CmdList();
135     for (const auto& cmd : cmds_list) {
136       subcmds.insert(cmd);
137     }
138   }
139   // duplication removed
140   return std::vector<std::string>{subcmds.begin(), subcmds.end()};
141 }
142 
CommandSequenceExecutorComponent()143 fruit::Component<CommandSequenceExecutor> CommandSequenceExecutorComponent() {
144   return fruit::createComponent()
145       .addMultibinding<LateInjected, CommandSequenceExecutor>();
146 }
147 
148 }  // namespace cuttlefish
149