• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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