• 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 
17 #include "host/commands/cvd/server_command/utils.h"
18 
19 #include "common/libs/fs/shared_buf.h"
20 #include "common/libs/utils/contains.h"
21 #include "common/libs/utils/files.h"
22 #include "common/libs/utils/flag_parser.h"
23 #include "common/libs/utils/users.h"
24 #include "host/commands/cvd/instance_manager.h"
25 #include "host/commands/cvd/server.h"
26 #include "host/libs/config/cuttlefish_config.h"
27 
28 namespace cuttlefish {
29 
ParseInvocation(const cvd::Request & request)30 CommandInvocation ParseInvocation(const cvd::Request& request) {
31   CommandInvocation invocation;
32   if (request.contents_case() != cvd::Request::ContentsCase::kCommandRequest) {
33     return invocation;
34   }
35   if (request.command_request().args_size() == 0) {
36     return invocation;
37   }
38   for (const std::string& arg : request.command_request().args()) {
39     invocation.arguments.push_back(arg);
40   }
41   invocation.arguments[0] = cpp_basename(invocation.arguments[0]);
42   if (invocation.arguments[0] == "cvd") {
43     invocation.command = invocation.arguments[1];
44     invocation.arguments.erase(invocation.arguments.begin());
45     invocation.arguments.erase(invocation.arguments.begin());
46   } else {
47     invocation.command = invocation.arguments[0];
48     invocation.arguments.erase(invocation.arguments.begin());
49   }
50   return invocation;
51 }
52 
VerifyPrecondition(const RequestWithStdio & request)53 Result<void> VerifyPrecondition(const RequestWithStdio& request) {
54   CF_EXPECT(
55       Contains(request.Message().command_request().env(), kAndroidHostOut),
56       "ANDROID_HOST_OUT in client environment is invalid.");
57   return {};
58 }
59 
ResponseFromSiginfo(siginfo_t infop)60 cuttlefish::cvd::Response ResponseFromSiginfo(siginfo_t infop) {
61   cvd::Response response;
62   response.mutable_command_response();  // set oneof field
63   auto& status = *response.mutable_status();
64   if (infop.si_code == CLD_EXITED && infop.si_status == 0) {
65     status.set_code(cvd::Status::OK);
66     return response;
67   }
68 
69   status.set_code(cvd::Status::INTERNAL);
70   std::string status_code_str = std::to_string(infop.si_status);
71   if (infop.si_code == CLD_EXITED) {
72     status.set_message("Exited with code " + status_code_str);
73   } else if (infop.si_code == CLD_KILLED) {
74     status.set_message("Exited with signal " + status_code_str);
75   } else {
76     status.set_message("Quit with code " + status_code_str);
77   }
78   return response;
79 }
80 
ConstructCommand(const ConstructCommandParam & param)81 Result<Command> ConstructCommand(const ConstructCommandParam& param) {
82   Command command(param.command_name);
83   command.SetExecutable(param.bin_path);
84   for (const std::string& arg : param.args) {
85     command.AddParameter(arg);
86   }
87   // Set CuttlefishConfig path based on assembly dir,
88   // used by subcommands when locating the CuttlefishConfig.
89   if (param.envs.count(cuttlefish::kCuttlefishConfigEnvVarName) == 0) {
90     auto config_path = InstanceManager::GetCuttlefishConfigPath(param.home);
91     if (config_path.ok()) {
92       command.AddEnvironmentVariable(cuttlefish::kCuttlefishConfigEnvVarName,
93                                      *config_path);
94     }
95   }
96   for (auto& it : param.envs) {
97     command.UnsetFromEnvironment(it.first);
98     command.AddEnvironmentVariable(it.first, it.second);
99   }
100   // Redirect stdin, stdout, stderr back to the cvd client
101   command.RedirectStdIO(Subprocess::StdIOChannel::kStdIn, param.in);
102   command.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, param.out);
103   command.RedirectStdIO(Subprocess::StdIOChannel::kStdErr, param.err);
104 
105   if (!param.working_dir.empty()) {
106     auto fd =
107         SharedFD::Open(param.working_dir, O_RDONLY | O_PATH | O_DIRECTORY);
108     CF_EXPECT(fd->IsOpen(), "Couldn't open \"" << param.working_dir
109                                                << "\": " << fd->StrError());
110     command.SetWorkingDirectory(fd);
111   }
112   return {std::move(command)};
113 }
114 
ConstructCvdHelpCommand(const std::string & bin_file,cvd_common::Envs envs,const std::vector<std::string> & subcmd_args,const RequestWithStdio & request)115 Result<Command> ConstructCvdHelpCommand(
116     const std::string& bin_file, cvd_common::Envs envs,
117     const std::vector<std::string>& subcmd_args,
118     const RequestWithStdio& request) {
119   const auto host_artifacts_path = envs.at("ANDROID_HOST_OUT");
120   const auto bin_path = host_artifacts_path + "/bin/" + bin_file;
121   auto client_pwd = request.Message().command_request().working_directory();
122   const auto home = (Contains(envs, "HOME") ? envs.at("HOME") : client_pwd);
123   cvd_common::Envs envs_copy{envs};
124   envs_copy["HOME"] = AbsolutePath(home);
125   envs[kAndroidSoongHostOut] = envs.at(kAndroidHostOut);
126   ConstructCommandParam construct_cmd_param{.bin_path = bin_path,
127                                             .home = home,
128                                             .args = subcmd_args,
129                                             .envs = std::move(envs_copy),
130                                             .working_dir = client_pwd,
131                                             .command_name = bin_file,
132                                             .in = request.In(),
133                                             .out = request.Out(),
134                                             .err = request.Err()};
135   Command help_command = CF_EXPECT(ConstructCommand(construct_cmd_param));
136   return help_command;
137 }
138 
ConstructCvdGenericNonHelpCommand(const ConstructNonHelpForm & request_form,const RequestWithStdio & request)139 Result<Command> ConstructCvdGenericNonHelpCommand(
140     const ConstructNonHelpForm& request_form, const RequestWithStdio& request) {
141   cvd_common::Envs envs{request_form.envs};
142   envs["HOME"] = request_form.home;
143   envs[kAndroidHostOut] = request_form.android_host_out;
144   envs[kAndroidSoongHostOut] = request_form.android_host_out;
145   const auto bin_path = ConcatToString(request_form.android_host_out, "/bin/",
146                                        request_form.bin_file);
147 
148   if (request_form.verbose) {
149     std::stringstream verbose_stream;
150     verbose_stream << "HOME=" << request_form.home << " ";
151     verbose_stream << kAndroidHostOut << "=" << envs.at(kAndroidHostOut) << " "
152                    << kAndroidSoongHostOut << "="
153                    << envs.at(kAndroidSoongHostOut) << " ";
154     verbose_stream << bin_path << "\\" << std::endl;
155     for (const auto& cmd_arg : request_form.cmd_args) {
156       verbose_stream << cmd_arg << " ";
157     }
158     if (!request_form.cmd_args.empty()) {
159       // remove trailing " ", and add a new line
160       verbose_stream.seekp(-1, std::ios_base::end);
161       verbose_stream << std::endl;
162     }
163     WriteAll(request.Err(), verbose_stream.str());
164   }
165   ConstructCommandParam construct_cmd_param{
166       .bin_path = bin_path,
167       .home = request_form.home,
168       .args = request_form.cmd_args,
169       .envs = envs,
170       .working_dir = request.Message().command_request().working_directory(),
171       .command_name = request_form.bin_file,
172       .in = request.In(),
173       .out = request.Out(),
174       .err = request.Err()};
175   return CF_EXPECT(ConstructCommand(construct_cmd_param));
176 }
177 
178 /*
179  * From external/gflags/src, commit:
180  *  061f68cd158fa658ec0b9b2b989ed55764870047
181  *
182  */
183 constexpr static std::array help_bool_opts{
184     "help", "helpfull", "helpshort", "helppackage", "helpxml", "version"};
185 constexpr static std::array help_str_opts{
186     "helpon",
187     "helpmatch",
188 };
189 
IsHelpSubcmd(const std::vector<std::string> & args)190 bool IsHelpSubcmd(const std::vector<std::string>& args) {
191   std::vector<std::string> copied_args(args);
192   std::vector<Flag> flags;
193   flags.reserve(help_bool_opts.size() + help_str_opts.size());
194   bool bool_value_placeholder = false;
195   std::string str_value_placeholder;
196   for (const auto bool_opt : help_bool_opts) {
197     flags.emplace_back(GflagsCompatFlag(bool_opt, bool_value_placeholder));
198   }
199   for (const auto str_opt : help_str_opts) {
200     flags.emplace_back(GflagsCompatFlag(str_opt, str_value_placeholder));
201   }
202   ParseFlags(flags, copied_args);
203   // if there was any match, some in copied_args were consumed.
204   return (args.size() != copied_args.size());
205 }
206 
207 }  // namespace cuttlefish
208