1 /*
2 * Copyright (C) 2023 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
17 #include "host/commands/cvd/server_command/display.h"
18
19 #include <android-base/strings.h>
20
21 #include <iostream>
22 #include <mutex>
23 #include <optional>
24 #include <sstream>
25 #include <string>
26 #include <vector>
27
28 #include "common/libs/fs/shared_buf.h"
29 #include "common/libs/utils/contains.h"
30 #include "common/libs/utils/subprocess.h"
31 #include "host/commands/cvd/flag.h"
32 #include "host/commands/cvd/selector/instance_database_types.h"
33 #include "host/commands/cvd/selector/instance_group_record.h"
34 #include "host/commands/cvd/selector/instance_record.h"
35 #include "host/commands/cvd/selector/selector_constants.h"
36 #include "host/commands/cvd/server_command/server_handler.h"
37 #include "host/commands/cvd/server_command/utils.h"
38 #include "host/commands/cvd/types.h"
39
40 namespace cuttlefish {
41
42 class CvdDisplayCommandHandler : public CvdServerHandler {
43 public:
INJECT(CvdDisplayCommandHandler (InstanceManager & instance_manager,SubprocessWaiter & subprocess_waiter))44 INJECT(CvdDisplayCommandHandler(InstanceManager& instance_manager,
45 SubprocessWaiter& subprocess_waiter))
46 : instance_manager_{instance_manager},
47 subprocess_waiter_(subprocess_waiter),
48 cvd_display_operations_{"display"} {}
49
CanHandle(const RequestWithStdio & request) const50 Result<bool> CanHandle(const RequestWithStdio& request) const {
51 auto invocation = ParseInvocation(request.Message());
52 return Contains(cvd_display_operations_, invocation.command);
53 }
54
Handle(const RequestWithStdio & request)55 Result<cvd::Response> Handle(const RequestWithStdio& request) override {
56 std::unique_lock interrupt_lock(interruptible_);
57 CF_EXPECT(!interrupted_, "Interrupted");
58 CF_EXPECT(CanHandle(request));
59 CF_EXPECT(VerifyPrecondition(request));
60 const uid_t uid = request.Credentials()->uid;
61 cvd_common::Envs envs =
62 cvd_common::ConvertToEnvs(request.Message().command_request().env());
63
64 auto [_, subcmd_args] = ParseInvocation(request.Message());
65
66 bool is_help = IsHelp(subcmd_args);
67 // may modify subcmd_args by consuming in parsing
68 Command command =
69 is_help ? CF_EXPECT(HelpCommand(request, uid, subcmd_args, envs))
70 : CF_EXPECT(NonHelpCommand(request, uid, subcmd_args, envs));
71 SubprocessOptions options;
72 CF_EXPECT(subprocess_waiter_.Setup(command.Start(options)));
73 interrupt_lock.unlock();
74
75 auto infop = CF_EXPECT(subprocess_waiter_.Wait());
76 return ResponseFromSiginfo(infop);
77 }
78
Interrupt()79 Result<void> Interrupt() override {
80 std::scoped_lock interrupt_lock(interruptible_);
81 interrupted_ = true;
82 CF_EXPECT(subprocess_waiter_.Interrupt());
83 return {};
84 }
85
CmdList() const86 cvd_common::Args CmdList() const override {
87 return cvd_common::Args(cvd_display_operations_.begin(),
88 cvd_display_operations_.end());
89 }
90
91 private:
HelpCommand(const RequestWithStdio & request,const uid_t uid,const cvd_common::Args & subcmd_args,cvd_common::Envs envs)92 Result<Command> HelpCommand(const RequestWithStdio& request, const uid_t uid,
93 const cvd_common::Args& subcmd_args,
94 cvd_common::Envs envs) {
95 CF_EXPECT(Contains(envs, kAndroidHostOut));
96 auto cvd_display_bin_path =
97 ConcatToString(envs.at(kAndroidHostOut), "/bin/", kDisplayBin);
98 std::string home = Contains(envs, "HOME")
99 ? envs.at("HOME")
100 : CF_EXPECT(SystemWideUserHome(uid));
101 envs["HOME"] = home;
102 envs[kAndroidSoongHostOut] = envs.at(kAndroidHostOut);
103 ConstructCommandParam construct_cmd_param{
104 .bin_path = cvd_display_bin_path,
105 .home = home,
106 .args = subcmd_args,
107 .envs = envs,
108 .working_dir = request.Message().command_request().working_directory(),
109 .command_name = kDisplayBin,
110 .in = request.In(),
111 .out = request.Out(),
112 .err = request.Err()};
113 Command command = CF_EXPECT(ConstructCommand(construct_cmd_param));
114 return command;
115 }
116
NonHelpCommand(const RequestWithStdio & request,const uid_t uid,cvd_common::Args & subcmd_args,cvd_common::Envs envs)117 Result<Command> NonHelpCommand(const RequestWithStdio& request,
118 const uid_t uid, cvd_common::Args& subcmd_args,
119 cvd_common::Envs envs) {
120 // test if there is --instance_num flag
121 CvdFlag<std::int32_t> instance_num_flag("instance_num");
122 auto instance_num_opt =
123 CF_EXPECT(instance_num_flag.FilterFlag(subcmd_args));
124 selector::Queries extra_queries;
125 if (instance_num_opt) {
126 extra_queries.emplace_back(selector::kInstanceIdField, *instance_num_opt);
127 }
128
129 const auto& selector_opts =
130 request.Message().command_request().selector_opts();
131 const auto selector_args = cvd_common::ConvertToArgs(selector_opts.args());
132
133 auto instance = CF_EXPECT(instance_manager_.SelectInstance(
134 selector_args, extra_queries, envs, uid));
135 const auto& instance_group = instance.ParentGroup();
136 const auto& home = instance_group.HomeDir();
137
138 const auto& android_host_out = instance_group.HostArtifactsPath();
139 auto cvd_display_bin_path =
140 ConcatToString(android_host_out, "/bin/", kDisplayBin);
141
142 cvd_common::Args cvd_env_args{subcmd_args};
143 cvd_env_args.push_back(
144 ConcatToString("--instance_num=", instance.InstanceId()));
145 envs["HOME"] = home;
146 envs[kAndroidHostOut] = android_host_out;
147 envs[kAndroidSoongHostOut] = android_host_out;
148
149 std::stringstream command_to_issue;
150 command_to_issue << "HOME=" << home << " " << kAndroidHostOut << "="
151 << android_host_out << " " << kAndroidSoongHostOut << "="
152 << android_host_out << " " << cvd_display_bin_path << " ";
153 for (const auto& arg : cvd_env_args) {
154 command_to_issue << arg << " ";
155 }
156 WriteAll(request.Err(), command_to_issue.str());
157
158 ConstructCommandParam construct_cmd_param{
159 .bin_path = cvd_display_bin_path,
160 .home = home,
161 .args = cvd_env_args,
162 .envs = envs,
163 .working_dir = request.Message().command_request().working_directory(),
164 .command_name = kDisplayBin,
165 .in = request.In(),
166 .out = request.Out(),
167 .err = request.Err()};
168 Command command = CF_EXPECT(ConstructCommand(construct_cmd_param));
169 return command;
170 }
171
IsHelp(const cvd_common::Args & cmd_args) const172 bool IsHelp(const cvd_common::Args& cmd_args) const {
173 // cvd display --help, --helpxml, etc or simply cvd display
174 if (cmd_args.empty() || IsHelpSubcmd(cmd_args)) {
175 return true;
176 }
177 // cvd display help <subcommand> format
178 return (cmd_args.front() == "help");
179 }
180
181 InstanceManager& instance_manager_;
182 SubprocessWaiter& subprocess_waiter_;
183 std::mutex interruptible_;
184 bool interrupted_ = false;
185 std::vector<std::string> cvd_display_operations_;
186 static constexpr char kDisplayBin[] = "cvd_internal_display";
187 };
188
189 fruit::Component<fruit::Required<InstanceManager, SubprocessWaiter>>
CvdDisplayComponent()190 CvdDisplayComponent() {
191 return fruit::createComponent()
192 .addMultibinding<CvdServerHandler, CvdDisplayCommandHandler>();
193 }
194
195 } // namespace cuttlefish
196