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
24 namespace cuttlefish {
25 namespace {
26
BashEscape(const std::string & input)27 std::string BashEscape(const std::string& input) {
28 bool safe = true;
29 for (const auto& c : input) {
30 if ('0' <= c && c <= '9') {
31 continue;
32 }
33 if ('a' <= c && c <= 'z') {
34 continue;
35 }
36 if ('A' <= c && c <= 'Z') {
37 continue;
38 }
39 if (c == '_' || c == '-' || c == '.' || c == ',' || c == '/') {
40 continue;
41 }
42 safe = false;
43 }
44 using android::base::StringReplace;
45 return safe ? input : "'" + StringReplace(input, "'", "\\'", true) + "'";
46 }
47
FormattedCommand(const cvd::CommandRequest command)48 std::string FormattedCommand(const cvd::CommandRequest command) {
49 std::stringstream effective_command;
50 effective_command << "Executing `";
51 for (const auto& [name, val] : command.env()) {
52 effective_command << BashEscape(name) << "=" << BashEscape(val) << " ";
53 }
54 for (const auto& argument : command.args()) {
55 effective_command << BashEscape(argument) << " ";
56 }
57 effective_command.seekp(-1, effective_command.cur);
58 effective_command << "`\n"; // Overwrite last space
59 return effective_command.str();
60 }
61
62 } // namespace
63
CommandSequenceExecutor(CvdCommandHandler & inner_handler)64 CommandSequenceExecutor::CommandSequenceExecutor(
65 CvdCommandHandler& inner_handler)
66 : inner_handler_(inner_handler) {}
67
Interrupt()68 Result<void> CommandSequenceExecutor::Interrupt() {
69 CF_EXPECT(inner_handler_.Interrupt());
70 return {};
71 }
72
Execute(const std::vector<RequestWithStdio> & requests,SharedFD report)73 Result<void> CommandSequenceExecutor::Execute(
74 const std::vector<RequestWithStdio>& requests, SharedFD report) {
75 std::unique_lock interrupt_lock(interrupt_mutex_);
76 if (interrupted_) {
77 return CF_ERR("Interrupted");
78 }
79 for (const auto& request : requests) {
80 auto& inner_proto = request.Message();
81 CF_EXPECT(inner_proto.has_command_request());
82 auto& command = inner_proto.command_request();
83 std::string str = FormattedCommand(command);
84 CF_EXPECT(WriteAll(report, str) == str.size(), report->StrError());
85
86 interrupt_lock.unlock();
87 auto response = CF_EXPECT(inner_handler_.Handle(request));
88 interrupt_lock.lock();
89 if (interrupted_) {
90 return CF_ERR("Interrupted");
91 }
92 CF_EXPECT(response.status().code() == cvd::Status::OK,
93 "Reason: \"" << response.status().message() << "\"");
94
95 static const char kDoneMsg[] = "Done\n";
96 CF_EXPECT(WriteAll(request.Err(), kDoneMsg) == sizeof(kDoneMsg) - 1,
97 request.Err()->StrError());
98 }
99 return {};
100 }
101
102 } // namespace cuttlefish
103