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/env.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_group_record.h"
33 #include "host/commands/cvd/selector/instance_record.h"
34 #include "host/commands/cvd/selector/selector_constants.h"
35 #include "host/commands/cvd/server_command/server_handler.h"
36 #include "host/commands/cvd/server_command/utils.h"
37 #include "host/commands/cvd/types.h"
38
39 namespace cuttlefish {
40
41 class CvdEnvCommandHandler : public CvdServerHandler {
42 public:
INJECT(CvdEnvCommandHandler (InstanceManager & instance_manager,SubprocessWaiter & subprocess_waiter))43 INJECT(CvdEnvCommandHandler(InstanceManager& instance_manager,
44 SubprocessWaiter& subprocess_waiter))
45 : instance_manager_{instance_manager},
46 subprocess_waiter_(subprocess_waiter),
47 cvd_env_operations_{"env"} {}
48
CanHandle(const RequestWithStdio & request) const49 Result<bool> CanHandle(const RequestWithStdio& request) const {
50 auto invocation = ParseInvocation(request.Message());
51 return Contains(cvd_env_operations_, invocation.command);
52 }
53
Handle(const RequestWithStdio & request)54 Result<cvd::Response> Handle(const RequestWithStdio& request) override {
55 std::unique_lock interrupt_lock(interruptible_);
56 CF_EXPECT(!interrupted_, "Interrupted");
57 CF_EXPECT(CanHandle(request));
58 CF_EXPECT(VerifyPrecondition(request));
59 const uid_t uid = request.Credentials()->uid;
60 cvd_common::Envs envs =
61 cvd_common::ConvertToEnvs(request.Message().command_request().env());
62
63 auto [_, subcmd_args] = ParseInvocation(request.Message());
64
65 /*
66 * cvd_env --help only. Not --helpxml, etc.
67 *
68 * Otherwise, IsHelpSubcmd() should be used here instead.
69 */
70 auto help_flag = CvdFlag("help", false);
71 cvd_common::Args subcmd_args_copy{subcmd_args};
72 auto help_parse_result = help_flag.CalculateFlag(subcmd_args_copy);
73 bool is_help = help_parse_result.ok() && (*help_parse_result);
74
75 Command command =
76 is_help ? CF_EXPECT(HelpCommand(request, subcmd_args, envs))
77 : CF_EXPECT(NonHelpCommand(request, uid, subcmd_args, envs));
78 SubprocessOptions options;
79 CF_EXPECT(subprocess_waiter_.Setup(command.Start(options)));
80 interrupt_lock.unlock();
81
82 auto infop = CF_EXPECT(subprocess_waiter_.Wait());
83 return ResponseFromSiginfo(infop);
84 }
85
Interrupt()86 Result<void> Interrupt() override {
87 std::scoped_lock interrupt_lock(interruptible_);
88 interrupted_ = true;
89 CF_EXPECT(subprocess_waiter_.Interrupt());
90 return {};
91 }
92
CmdList() const93 cvd_common::Args CmdList() const override {
94 return cvd_common::Args(cvd_env_operations_.begin(),
95 cvd_env_operations_.end());
96 }
97
98 private:
HelpCommand(const RequestWithStdio & request,const cvd_common::Args & subcmd_args,const cvd_common::Envs & envs)99 Result<Command> HelpCommand(const RequestWithStdio& request,
100 const cvd_common::Args& subcmd_args,
101 const cvd_common::Envs& envs) {
102 CF_EXPECT(Contains(envs, kAndroidHostOut));
103 return CF_EXPECT(
104 ConstructCvdHelpCommand(kCvdEnvBin, envs, subcmd_args, request));
105 }
106
NonHelpCommand(const RequestWithStdio & request,const uid_t uid,const cvd_common::Args & subcmd_args,const cvd_common::Envs & envs)107 Result<Command> NonHelpCommand(const RequestWithStdio& request,
108 const uid_t uid,
109 const cvd_common::Args& subcmd_args,
110 const cvd_common::Envs& envs) {
111 const auto& selector_opts =
112 request.Message().command_request().selector_opts();
113 const auto selector_args = cvd_common::ConvertToArgs(selector_opts.args());
114
115 auto instance =
116 CF_EXPECT(instance_manager_.SelectInstance(selector_args, envs, uid));
117 const auto& instance_group = instance.ParentGroup();
118 const auto& home = instance_group.HomeDir();
119
120 const auto& android_host_out = instance_group.HostArtifactsPath();
121 auto cvd_env_bin_path =
122 ConcatToString(android_host_out, "/bin/", kCvdEnvBin);
123 const auto& internal_device_name = instance.InternalDeviceName();
124
125 cvd_common::Args cvd_env_args{internal_device_name};
126 cvd_env_args.insert(cvd_env_args.end(), subcmd_args.begin(),
127 subcmd_args.end());
128
129 return CF_EXPECT(
130 ConstructCvdGenericNonHelpCommand({.bin_file = kCvdEnvBin,
131 .envs = envs,
132 .cmd_args = cvd_env_args,
133 .android_host_out = android_host_out,
134 .home = home,
135 .verbose = true},
136 request));
137 }
138
139 InstanceManager& instance_manager_;
140 SubprocessWaiter& subprocess_waiter_;
141 std::mutex interruptible_;
142 bool interrupted_ = false;
143 std::vector<std::string> cvd_env_operations_;
144
145 static constexpr char kCvdEnvBin[] = "cvd_internal_env";
146 };
147
148 fruit::Component<fruit::Required<InstanceManager, SubprocessWaiter>>
CvdEnvComponent()149 CvdEnvComponent() {
150 return fruit::createComponent()
151 .addMultibinding<CvdServerHandler, CvdEnvCommandHandler>();
152 }
153
154 } // namespace cuttlefish
155