• 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/start.h"
18 
19 #include <sys/types.h>
20 
21 #include <array>
22 #include <atomic>
23 #include <cstdint>
24 #include <cstdlib>
25 #include <fstream>
26 #include <iostream>
27 #include <map>
28 #include <mutex>
29 #include <regex>
30 #include <sstream>
31 #include <string>
32 
33 #include <android-base/parseint.h>
34 #include <android-base/strings.h>
35 
36 #include "common/libs/fs/shared_buf.h"
37 #include "common/libs/fs/shared_fd.h"
38 #include "common/libs/utils/contains.h"
39 #include "common/libs/utils/files.h"
40 #include "common/libs/utils/flag_parser.h"
41 #include "common/libs/utils/result.h"
42 #include "cvd_server.pb.h"
43 #include "host/commands/cvd/command_sequence.h"
44 #include "host/commands/cvd/common_utils.h"
45 #include "host/commands/cvd/selector/selector_constants.h"
46 #include "host/commands/cvd/server_command/generic.h"
47 #include "host/commands/cvd/server_command/server_handler.h"
48 #include "host/commands/cvd/server_command/start_impl.h"
49 #include "host/commands/cvd/server_command/subprocess_waiter.h"
50 #include "host/commands/cvd/server_command/utils.h"
51 #include "host/commands/cvd/types.h"
52 #include "host/libs/config/cuttlefish_config.h"
53 
54 namespace cuttlefish {
55 
56 class CvdStartCommandHandler : public CvdServerHandler {
57  public:
INJECT(CvdStartCommandHandler (InstanceManager & instance_manager,HostToolTargetManager & host_tool_target_manager))58   INJECT(
59       CvdStartCommandHandler(InstanceManager& instance_manager,
60                              HostToolTargetManager& host_tool_target_manager))
61       : instance_manager_(instance_manager),
62         host_tool_target_manager_(host_tool_target_manager),
63         acloud_action_ended_(false) {}
64 
65   Result<bool> CanHandle(const RequestWithStdio& request) const;
66   Result<cvd::Response> Handle(const RequestWithStdio& request) override;
67   Result<void> Interrupt() override;
68   std::vector<std::string> CmdList() const override;
69 
70  private:
71   Result<void> UpdateInstanceDatabase(
72       const uid_t uid, const selector::GroupCreationInfo& group_creation_info);
73   Result<void> FireCommand(Command&& command, const bool wait);
74   bool HasHelpOpts(const cvd_common::Args& args) const;
75 
76   Result<Command> ConstructCvdNonHelpCommand(
77       const std::string& bin_file,
78       const selector::GroupCreationInfo& group_info,
79       const RequestWithStdio& request);
80 
81   // call this only if !is_help
82   Result<selector::GroupCreationInfo> GetGroupCreationInfo(
83       const std::string& start_bin, const std::string& subcmd,
84       const cvd_common::Args& subcmd_args, const cvd_common::Envs& envs,
85       const RequestWithStdio& request);
86 
87   Result<cvd::Response> FillOutNewInstanceInfo(
88       cvd::Response&& response,
89       const selector::GroupCreationInfo& group_creation_info);
90 
91   struct UpdatedArgsAndEnvs {
92     cvd_common::Args args;
93     cvd_common::Envs envs;
94   };
95   Result<UpdatedArgsAndEnvs> UpdateInstanceArgsAndEnvs(
96       cvd_common::Args&& args, cvd_common::Envs&& envs,
97       const std::vector<selector::PerInstanceInfo>& instances,
98       const std::string& artifacts_path, const std::string& start_bin);
99 
100   static Result<std::vector<std::string>> UpdateWebrtcDeviceId(
101       std::vector<std::string>&& args, const std::string& group_name,
102       const std::vector<selector::PerInstanceInfo>& per_instance_info);
103 
104   Result<selector::GroupCreationInfo> UpdateArgsAndEnvs(
105       selector::GroupCreationInfo&& old_group_info,
106       const std::string& start_bin);
107 
108   Result<std::string> FindStartBin(const std::string& android_host_out);
109 
110   Result<void> SetBuildId(const uid_t uid, const std::string& group_name,
111                           const std::string& home);
112 
113   static void MarkLockfiles(selector::GroupCreationInfo& group_info,
114                             const InUseState state);
MarkLockfilesInUse(selector::GroupCreationInfo & group_info)115   static void MarkLockfilesInUse(selector::GroupCreationInfo& group_info) {
116     MarkLockfiles(group_info, InUseState::kInUse);
117   }
118 
119   Result<void> HandleNoDaemonWorker(
120       const selector::GroupCreationInfo& group_creation_info,
121       std::atomic<bool>* interrupted, const uid_t uid);
122 
123   Result<cvd::Response> HandleNoDaemon(
124       const std::optional<selector::GroupCreationInfo>& group_creation_info,
125       const uid_t uid);
126   Result<cvd::Response> HandleDaemon(
127       std::optional<selector::GroupCreationInfo>& group_creation_info,
128       const uid_t uid);
129   Result<void> AcloudCompatActions(
130       const selector::GroupCreationInfo& group_creation_info,
131       const RequestWithStdio& request);
132 
133   InstanceManager& instance_manager_;
134   SubprocessWaiter subprocess_waiter_;
135   HostToolTargetManager& host_tool_target_manager_;
136   CommandSequenceExecutor command_executor_;
137   std::mutex interruptible_;
138   bool interrupted_ = false;
139   /*
140    * Used by Interrupt() not to call command_executor_.Interrupt()
141    *
142    * If true, it is guaranteed that the command_executor_ ended the execution.
143    * If false, it may or may not be after the command_executor_.Execute()
144    */
145   std::atomic<bool> acloud_action_ended_;
146   static const std::array<std::string, 2> supported_commands_;
147 };
148 
GenericNestedHandlerComponent(InstanceManager * instance_manager,HostToolTargetManager * host_tool_target_manager,SubprocessWaiter * subprocess_waiter_for_nested_handler)149 fruit::Component<> GenericNestedHandlerComponent(
150     InstanceManager* instance_manager,
151     HostToolTargetManager* host_tool_target_manager,
152     SubprocessWaiter* subprocess_waiter_for_nested_handler) {
153   return fruit::createComponent()
154       .bindInstance(*instance_manager)
155       .bindInstance(*host_tool_target_manager)
156       .bindInstance(*subprocess_waiter_for_nested_handler)
157       .install(cvdGenericCommandComponent);
158 }
159 
AcloudCompatActions(const selector::GroupCreationInfo & group_creation_info,const RequestWithStdio & request)160 Result<void> CvdStartCommandHandler::AcloudCompatActions(
161     const selector::GroupCreationInfo& group_creation_info,
162     const RequestWithStdio& request) {
163   std::unique_lock interrupt_lock(interruptible_);
164   CF_EXPECT(!interrupted_, "Interrupted");
165   // rm -fr "TempDir()/acloud_cvd_temp/local-instance-<i>"
166   std::string acloud_compat_home_prefix =
167       TempDir() + "/acloud_cvd_temp/local-instance-";
168   std::vector<std::string> acloud_compat_homes;
169   acloud_compat_homes.reserve(group_creation_info.instances.size());
170   for (const auto instance : group_creation_info.instances) {
171     acloud_compat_homes.push_back(
172         ConcatToString(acloud_compat_home_prefix, instance.instance_id_));
173   }
174   for (const auto acloud_compat_home : acloud_compat_homes) {
175     bool result_deleted = true;
176     std::stringstream acloud_compat_home_stream;
177     if (!FileExists(acloud_compat_home)) {
178       continue;
179     }
180     if (!Contains(group_creation_info.envs, kLaunchedByAcloud) ||
181         group_creation_info.envs.at(kLaunchedByAcloud) != "true") {
182       if (!DirectoryExists(acloud_compat_home, /*follow_symlinks=*/false)) {
183         // cvd created a symbolic link
184         result_deleted = RemoveFile(acloud_compat_home);
185       } else {
186         // acloud created a directory
187         // rm -fr isn't supporetd by TreeHugger, so if we fork-and-exec to
188         // literally run "rm -fr", the presubmit testing may fail if ever this
189         // code is tested in the future.
190         result_deleted = RecursivelyRemoveDirectory(acloud_compat_home);
191       }
192     }
193     if (!result_deleted) {
194       LOG(ERROR) << "Removing " << acloud_compat_home << " failed.";
195       continue;
196     }
197   }
198 
199   // ln -f -s  [target] [symlink]
200   // 1. mkdir -p home
201   // 2. ln -f -s android_host_out home/host_bins
202   // 3. for each i in ids,
203   //     ln -f -s home /tmp/acloud_cvd_temp/local-instance-<i>
204   std::vector<MakeRequestForm> request_forms;
205   const cvd_common::Envs& common_envs = group_creation_info.envs;
206 
207   const std::string& home_dir = group_creation_info.home;
208   const std::string client_pwd =
209       request.Message().command_request().working_directory();
210   request_forms.push_back(
211       {.working_dir = client_pwd,
212        .cmd_args = cvd_common::Args{"mkdir", "-p", home_dir},
213        .env = common_envs,
214        .selector_args = cvd_common::Args{}});
215   const std::string& android_host_out = group_creation_info.host_artifacts_path;
216   request_forms.push_back(
217       {.working_dir = client_pwd,
218        .cmd_args = cvd_common::Args{"ln", "-T", "-f", "-s", android_host_out,
219                                     home_dir + "/host_bins"},
220        .env = common_envs,
221        .selector_args = cvd_common::Args{}});
222   /* TODO(weihsu@): cvd acloud delete/list must handle multi-tenancy gracefully
223    *
224    * acloud delete just calls, for all instances in a group,
225    *  /tmp/acloud_cvd_temp/local-instance-<i>/host_bins/stop_cvd
226    *
227    * That isn't necessary. Not desirable. Cvd acloud should read the instance
228    * manager's in-memory data structure, and call stop_cvd once for the entire
229    * group.
230    *
231    * Likewise, acloud list simply shows all instances in a flattened way. The
232    * user has no clue about an instance group. Cvd acloud should show the
233    * hierarchy.
234    *
235    * For now, we create the symbolic links so that it is compatible with acloud
236    * in Python.
237    */
238   for (const auto& acloud_compat_home : acloud_compat_homes) {
239     if (acloud_compat_home == home_dir) {
240       LOG(ERROR) << "The \"HOME\" directory is acloud workspace, which will "
241                  << "be deleted by next cvd start or acloud command with the"
242                  << " same directory being \"HOME\"";
243       continue;
244     }
245     request_forms.push_back({
246         .working_dir = client_pwd,
247         .cmd_args = cvd_common::Args{"ln", "-T", "-f", "-s", home_dir,
248                                      acloud_compat_home},
249         .env = common_envs,
250         .selector_args = cvd_common::Args{},
251     });
252   }
253   std::vector<cvd::Request> request_protos;
254   for (const auto& request_form : request_forms) {
255     request_protos.emplace_back(MakeRequest(request_form));
256   }
257   std::vector<RequestWithStdio> new_requests;
258   auto dev_null = SharedFD::Open("/dev/null", O_RDWR);
259   CF_EXPECT(dev_null->IsOpen(), dev_null->StrError());
260   std::vector<SharedFD> dev_null_fds = {dev_null, dev_null, dev_null};
261   for (auto& request_proto : request_protos) {
262     new_requests.emplace_back(request.Client(), request_proto, dev_null_fds,
263                               request.Credentials());
264   }
265   SubprocessWaiter subprocess_waiter;
266   // injector only with the GenericCommandHandler for ln and mkdir
267   fruit::Injector<> injector(GenericNestedHandlerComponent,
268                              std::addressof(this->instance_manager_),
269                              std::addressof(this->host_tool_target_manager_),
270                              std::addressof(subprocess_waiter));
271   CF_EXPECT(command_executor_.LateInject(injector),
272             "Creating local CommandSequenceExecutor in cvd start failed.");
273   interrupt_lock.unlock();
274   CF_EXPECT(command_executor_.Execute(new_requests, dev_null));
275   return {};
276 }
277 
MarkLockfiles(selector::GroupCreationInfo & group_info,const InUseState state)278 void CvdStartCommandHandler::MarkLockfiles(
279     selector::GroupCreationInfo& group_info, const InUseState state) {
280   auto& instances = group_info.instances;
281   for (auto& instance : instances) {
282     if (!instance.instance_file_lock_) {
283       continue;
284     }
285     auto result = instance.instance_file_lock_->Status(state);
286     if (!result.ok()) {
287       LOG(ERROR) << result.error().Message();
288     }
289   }
290 }
291 
CanHandle(const RequestWithStdio & request) const292 Result<bool> CvdStartCommandHandler::CanHandle(
293     const RequestWithStdio& request) const {
294   auto invocation = ParseInvocation(request.Message());
295   return Contains(supported_commands_, invocation.command);
296 }
297 
298 Result<CvdStartCommandHandler::UpdatedArgsAndEnvs>
UpdateInstanceArgsAndEnvs(cvd_common::Args && args,cvd_common::Envs && envs,const std::vector<selector::PerInstanceInfo> & instances,const std::string & artifacts_path,const std::string & start_bin)299 CvdStartCommandHandler::UpdateInstanceArgsAndEnvs(
300     cvd_common::Args&& args, cvd_common::Envs&& envs,
301     const std::vector<selector::PerInstanceInfo>& instances,
302     const std::string& artifacts_path, const std::string& start_bin) {
303   std::vector<unsigned> ids;
304   ids.reserve(instances.size());
305   for (const auto& instance : instances) {
306     ids.emplace_back(instance.instance_id_);
307   }
308 
309   cvd_common::Args new_args{std::move(args)};
310   std::string old_instance_nums;
311   std::string old_num_instances;
312   std::string old_base_instance_num;
313 
314   std::vector<Flag> instance_id_flags{
315       GflagsCompatFlag("instance_nums", old_instance_nums),
316       GflagsCompatFlag("num_instances", old_num_instances),
317       GflagsCompatFlag("base_instance_num", old_base_instance_num)};
318   // discard old ones
319   ParseFlags(instance_id_flags, new_args);
320 
321   auto check_flag = [artifacts_path, start_bin,
322                      this](const std::string& flag_name) -> Result<void> {
323     CF_EXPECT(
324         host_tool_target_manager_.ReadFlag({.artifacts_path = artifacts_path,
325                                             .op = "start",
326                                             .flag_name = flag_name}));
327     return {};
328   };
329   auto max = *(std::max_element(ids.cbegin(), ids.cend()));
330   auto min = *(std::min_element(ids.cbegin(), ids.cend()));
331 
332   const bool is_consecutive = ((max - min) == (ids.size() - 1));
333   const bool is_sorted = std::is_sorted(ids.begin(), ids.end());
334 
335   if (!is_consecutive || !is_sorted) {
336     std::string flag_value = android::base::Join(ids, ",");
337     CF_EXPECT(check_flag("instance_nums"));
338     new_args.emplace_back("--instance_nums=" + flag_value);
339     return UpdatedArgsAndEnvs{.args = std::move(new_args),
340                               .envs = std::move(envs)};
341   }
342 
343   // sorted and consecutive, so let's use old flags
344   // like --num_instances and --base_instance_num
345   if (ids.size() > 1) {
346     CF_EXPECT(check_flag("num_instances"),
347               "--num_instances is not supported but multi-tenancy requested.");
348     new_args.emplace_back("--num_instances=" + std::to_string(ids.size()));
349   }
350   cvd_common::Envs new_envs{std::move(envs)};
351   if (check_flag("base_instance_num").ok()) {
352     new_args.emplace_back("--base_instance_num=" + std::to_string(min));
353   }
354   new_envs[kCuttlefishInstanceEnvVarName] = std::to_string(min);
355   return UpdatedArgsAndEnvs{.args = std::move(new_args),
356                             .envs = std::move(new_envs)};
357 }
358 
359 /*
360  * Adds --webrtc_device_id when necessary to cmd_args_
361  */
UpdateWebrtcDeviceId(std::vector<std::string> && args,const std::string & group_name,const std::vector<selector::PerInstanceInfo> & per_instance_info)362 Result<std::vector<std::string>> CvdStartCommandHandler::UpdateWebrtcDeviceId(
363     std::vector<std::string>&& args, const std::string& group_name,
364     const std::vector<selector::PerInstanceInfo>& per_instance_info) {
365   std::vector<std::string> new_args{std::move(args)};
366   // consume webrtc_device_id
367   // it was verified by start_selector_parser
368   std::string flag_value;
369   std::vector<Flag> webrtc_device_id_flag{
370       GflagsCompatFlag("webrtc_device_id", flag_value)};
371   CF_EXPECT(ParseFlags(webrtc_device_id_flag, new_args));
372 
373   CF_EXPECT(!group_name.empty());
374   std::vector<std::string> device_name_list;
375   device_name_list.reserve(per_instance_info.size());
376   for (const auto& instance : per_instance_info) {
377     const auto& per_instance_name = instance.per_instance_name_;
378     std::string device_name{group_name};
379     device_name.append("-").append(per_instance_name);
380     device_name_list.emplace_back(device_name);
381   }
382   // take --webrtc_device_id flag away
383   new_args.emplace_back("--webrtc_device_id=" +
384                         android::base::Join(device_name_list, ","));
385   return new_args;
386 }
387 
ConstructCvdNonHelpCommand(const std::string & bin_file,const selector::GroupCreationInfo & group_info,const RequestWithStdio & request)388 Result<Command> CvdStartCommandHandler::ConstructCvdNonHelpCommand(
389     const std::string& bin_file, const selector::GroupCreationInfo& group_info,
390     const RequestWithStdio& request) {
391   auto bin_path = group_info.host_artifacts_path;
392   bin_path.append("/bin/").append(bin_file);
393   CF_EXPECT(!group_info.home.empty());
394   ConstructCommandParam construct_cmd_param{
395       .bin_path = bin_path,
396       .home = group_info.home,
397       .args = group_info.args,
398       .envs = group_info.envs,
399       .working_dir = request.Message().command_request().working_directory(),
400       .command_name = bin_file,
401       .in = request.In(),
402       .out = request.Out(),
403       .err = request.Err()};
404   Command non_help_command = CF_EXPECT(ConstructCommand(construct_cmd_param));
405   return non_help_command;
406 }
407 
408 // call this only if !is_help
409 Result<selector::GroupCreationInfo>
GetGroupCreationInfo(const std::string & start_bin,const std::string & subcmd,const std::vector<std::string> & subcmd_args,const cvd_common::Envs & envs,const RequestWithStdio & request)410 CvdStartCommandHandler::GetGroupCreationInfo(
411     const std::string& start_bin, const std::string& subcmd,
412     const std::vector<std::string>& subcmd_args, const cvd_common::Envs& envs,
413     const RequestWithStdio& request) {
414   using CreationAnalyzerParam =
415       selector::CreationAnalyzer::CreationAnalyzerParam;
416   const auto& selector_opts =
417       request.Message().command_request().selector_opts();
418   const auto selector_args = cvd_common::ConvertToArgs(selector_opts.args());
419   CreationAnalyzerParam analyzer_param{
420       .cmd_args = subcmd_args, .envs = envs, .selector_args = selector_args};
421   auto cred = CF_EXPECT(request.Credentials());
422   auto group_creation_info =
423       CF_EXPECT(instance_manager_.Analyze(subcmd, analyzer_param, cred));
424   auto final_group_creation_info =
425       CF_EXPECT(UpdateArgsAndEnvs(std::move(group_creation_info), start_bin));
426   return final_group_creation_info;
427 }
428 
UpdateArgsAndEnvs(selector::GroupCreationInfo && old_group_info,const std::string & start_bin)429 Result<selector::GroupCreationInfo> CvdStartCommandHandler::UpdateArgsAndEnvs(
430     selector::GroupCreationInfo&& old_group_info,
431     const std::string& start_bin) {
432   selector::GroupCreationInfo group_creation_info = std::move(old_group_info);
433   // update instance related-flags, envs
434   const auto& instances = group_creation_info.instances;
435   const auto& host_artifacts_path = group_creation_info.host_artifacts_path;
436   auto [new_args, new_envs] = CF_EXPECT(UpdateInstanceArgsAndEnvs(
437       std::move(group_creation_info.args), std::move(group_creation_info.envs),
438       instances, host_artifacts_path, start_bin));
439   group_creation_info.args = std::move(new_args);
440   group_creation_info.envs = std::move(new_envs);
441 
442   auto webrtc_device_id_flag = host_tool_target_manager_.ReadFlag(
443       {.artifacts_path = group_creation_info.host_artifacts_path,
444        .op = "start",
445        .flag_name = "webrtc_device_id"});
446   if (webrtc_device_id_flag.ok()) {
447     group_creation_info.args = CF_EXPECT(UpdateWebrtcDeviceId(
448         std::move(group_creation_info.args), group_creation_info.group_name,
449         group_creation_info.instances));
450   }
451 
452   group_creation_info.envs["HOME"] = group_creation_info.home;
453   group_creation_info.envs[kAndroidHostOut] =
454       group_creation_info.host_artifacts_path;
455   group_creation_info.envs[kAndroidProductOut] =
456       group_creation_info.product_out_path;
457   /* b/253644566
458    *
459    * Old branches used kAndroidSoongHostOut instead of kAndroidHostOut
460    */
461   group_creation_info.envs[kAndroidSoongHostOut] =
462       group_creation_info.host_artifacts_path;
463   group_creation_info.envs[kCvdMarkEnv] = "true";
464   return group_creation_info;
465 }
466 
operator <<(std::ostream & out,const cvd_common::Args & v)467 static std::ostream& operator<<(std::ostream& out, const cvd_common::Args& v) {
468   if (v.empty()) {
469     return out;
470   }
471   for (int i = 0; i < v.size() - 1; i++) {
472     out << v.at(i) << " ";
473   }
474   out << v.back();
475   return out;
476 }
477 
ShowLaunchCommand(const std::string & bin,const cvd_common::Args & args,const cvd_common::Envs & envs)478 static void ShowLaunchCommand(const std::string& bin,
479                               const cvd_common::Args& args,
480                               const cvd_common::Envs& envs) {
481   std::stringstream ss;
482   std::vector<std::string> interesting_env_names{"HOME",
483                                                  kAndroidHostOut,
484                                                  kAndroidSoongHostOut,
485                                                  "ANDROID_PRODUCT_OUT",
486                                                  kCuttlefishInstanceEnvVarName,
487                                                  kCuttlefishConfigEnvVarName};
488   for (const auto& interesting_env_name : interesting_env_names) {
489     if (Contains(envs, interesting_env_name)) {
490       ss << interesting_env_name << "=\"" << envs.at(interesting_env_name)
491          << "\" ";
492     }
493   }
494   ss << " " << bin << " " << args;
495   LOG(ERROR) << "launcher command: " << ss.str();
496 }
497 
ShowLaunchCommand(const std::string & bin,selector::GroupCreationInfo & group_info)498 static void ShowLaunchCommand(const std::string& bin,
499                               selector::GroupCreationInfo& group_info) {
500   ShowLaunchCommand(bin, group_info.args, group_info.envs);
501 }
502 
FindStartBin(const std::string & android_host_out)503 Result<std::string> CvdStartCommandHandler::FindStartBin(
504     const std::string& android_host_out) {
505   auto start_bin = CF_EXPECT(host_tool_target_manager_.ExecBaseName({
506       .artifacts_path = android_host_out,
507       .op = "start",
508   }));
509   return start_bin;
510 }
511 
512 // std::string -> bool
513 enum class BoolValueType : std::uint8_t { kTrue = 0, kFalse, kUnknown };
IsDaemonModeFlag(const cvd_common::Args & args)514 static Result<bool> IsDaemonModeFlag(const cvd_common::Args& args) {
515   /*
516    * --daemon could be either bool or string flags.
517    */
518   bool is_daemon = false;
519   auto initial_size = args.size();
520   Flag daemon_bool = GflagsCompatFlag("daemon", is_daemon);
521   std::vector<Flag> as_bool_flags{daemon_bool};
522   cvd_common::Args copied_args{args};
523   if (ParseFlags(as_bool_flags, copied_args)) {
524     if (initial_size != copied_args.size()) {
525       return is_daemon;
526     }
527   }
528   std::string daemon_values;
529   Flag daemon_string = GflagsCompatFlag("daemon", daemon_values);
530   cvd_common::Args copied_args2{args};
531   std::vector<Flag> as_string_flags{daemon_string};
532   if (!ParseFlags(as_string_flags, copied_args2)) {
533     return false;
534   }
535   if (initial_size == copied_args2.size()) {
536     return false;  // not consumed
537   }
538   // --daemon should have been handled above
539   CF_EXPECT(!daemon_values.empty());
540   std::unordered_set<std::string> true_strings = {"y", "yes", "true"};
541   std::unordered_set<std::string> false_strings = {"n", "no", "false"};
542   auto tokens = android::base::Tokenize(daemon_values, ",");
543   std::unordered_set<BoolValueType> value_set;
544   for (const auto& token : tokens) {
545     std::string daemon_value(token);
546     /*
547      * https://en.cppreference.com/w/cpp/string/byte/tolower
548      *
549      * char should be converted to unsigned char first.
550      */
551     std::transform(daemon_value.begin(), daemon_value.end(),
552                    daemon_value.begin(),
553                    [](unsigned char c) { return std::tolower(c); });
554     if (Contains(true_strings, daemon_value)) {
555       value_set.insert(BoolValueType::kTrue);
556       continue;
557     }
558     if (Contains(false_strings, daemon_value)) {
559       value_set.insert(BoolValueType::kFalse);
560     } else {
561       value_set.insert(BoolValueType::kUnknown);
562     }
563   }
564   CF_EXPECT_LE(value_set.size(), 1,
565                "Vectorized flags for --daemon is not supported by cvd");
566   const auto only_element = *(value_set.begin());
567   // We want to, basically, launch with daemon mode, and want to know
568   // when we must not do so
569   if (only_element == BoolValueType::kFalse) {
570     return false;
571   }
572   // if kUnknown, the launcher will fail. Which mode doesn't matter
573   // for the launcher. But it matters for cvd in how cvd handles the
574   // failure.
575   return true;
576 }
577 
Handle(const RequestWithStdio & request)578 Result<cvd::Response> CvdStartCommandHandler::Handle(
579     const RequestWithStdio& request) {
580   std::unique_lock interrupt_lock(interruptible_);
581   if (interrupted_) {
582     return CF_ERR("Interrupted");
583   }
584   CF_EXPECT(CanHandle(request));
585 
586   cvd::Response response;
587   response.mutable_command_response();
588 
589   auto precondition_verified = VerifyPrecondition(request);
590   if (!precondition_verified.ok()) {
591     response.mutable_status()->set_code(cvd::Status::FAILED_PRECONDITION);
592     response.mutable_status()->set_message(
593         precondition_verified.error().Message());
594     return response;
595   }
596 
597   const uid_t uid = request.Credentials()->uid;
598   cvd_common::Envs envs =
599       cvd_common::ConvertToEnvs(request.Message().command_request().env());
600   if (Contains(envs, "HOME")) {
601     if (envs.at("HOME").empty()) {
602       envs.erase("HOME");
603     } else {
604       // As the end-user may override HOME, this could be a relative path
605       // to client's pwd, or may include "~" which is the client's actual
606       // home directory.
607       auto client_pwd = request.Message().command_request().working_directory();
608       const auto given_home_dir = envs.at("HOME");
609       /*
610        * Imagine this scenario:
611        *   client$ export HOME=/tmp/new/dir
612        *   client$ HOME="~/subdir" cvd start
613        *
614        * The value of ~ isn't sent to the server. The server can't figure that
615        * out as it might be overridden before the cvd start command.
616        */
617       CF_EXPECT(!android::base::StartsWith(given_home_dir, "~") &&
618                     !android::base::StartsWith(given_home_dir, "~/"),
619                 "The HOME directory should not start with ~");
620       envs["HOME"] = CF_EXPECT(
621           EmulateAbsolutePath({.current_working_dir = client_pwd,
622                                .home_dir = CF_EXPECT(SystemWideUserHome(uid)),
623                                .path_to_convert = given_home_dir,
624                                .follow_symlink = false}));
625     }
626   }
627   CF_EXPECT(Contains(envs, kAndroidHostOut));
628   const auto bin = CF_EXPECT(FindStartBin(envs.at(kAndroidHostOut)));
629 
630   // update DB if not help
631   // collect group creation infos
632   auto [subcmd, subcmd_args] = ParseInvocation(request.Message());
633   CF_EXPECT(Contains(supported_commands_, subcmd),
634             "subcmd should be start but is " << subcmd);
635   const bool is_help = HasHelpOpts(subcmd_args);
636   const bool is_daemon = CF_EXPECT(IsDaemonModeFlag(subcmd_args));
637 
638   std::optional<selector::GroupCreationInfo> group_creation_info;
639   if (!is_help) {
640     group_creation_info = CF_EXPECT(
641         GetGroupCreationInfo(bin, subcmd, subcmd_args, envs, request));
642     CF_EXPECT(UpdateInstanceDatabase(uid, *group_creation_info));
643     response = CF_EXPECT(
644         FillOutNewInstanceInfo(std::move(response), *group_creation_info));
645   }
646 
647   Command command =
648       is_help
649           ? CF_EXPECT(ConstructCvdHelpCommand(bin, envs, subcmd_args, request))
650           : CF_EXPECT(
651                 ConstructCvdNonHelpCommand(bin, *group_creation_info, request));
652 
653   if (!is_help) {
654     CF_EXPECT(
655         group_creation_info != std::nullopt,
656         "group_creation_info should be nullopt only when --help is given.");
657   }
658 
659   if (is_help) {
660     ShowLaunchCommand(command.Executable(), subcmd_args, envs);
661   } else {
662     ShowLaunchCommand(command.Executable(), *group_creation_info);
663     CF_EXPECT(request.Message().command_request().wait_behavior() !=
664               cvd::WAIT_BEHAVIOR_START);
665   }
666 
667   FireCommand(std::move(command), /*should_wait*/ true);
668   interrupt_lock.unlock();
669 
670   if (is_help) {
671     auto infop = CF_EXPECT(subprocess_waiter_.Wait());
672     return ResponseFromSiginfo(infop);
673   }
674 
675   // make acquire interrupt_lock inside.
676   auto acloud_compat_action_result =
677       AcloudCompatActions(*group_creation_info, request);
678   acloud_action_ended_ = true;
679   if (!acloud_compat_action_result.ok()) {
680     LOG(ERROR) << acloud_compat_action_result.error().Trace();
681     LOG(ERROR) << "AcloudCompatActions() failed"
682                << " but continue as they are minor errors.";
683   }
684   return is_daemon ? HandleDaemon(group_creation_info, uid)
685                    : HandleNoDaemon(group_creation_info, uid);
686 }
687 
HandleNoDaemonWorker(const selector::GroupCreationInfo & group_creation_info,std::atomic<bool> * interrupted,const uid_t uid)688 Result<void> CvdStartCommandHandler::HandleNoDaemonWorker(
689     const selector::GroupCreationInfo& group_creation_info,
690     std::atomic<bool>* interrupted, const uid_t uid) {
691   const std::string home_dir = group_creation_info.home;
692   const std::string group_name = group_creation_info.group_name;
693   std::string kernel_log_path =
694       ConcatToString(home_dir, "/cuttlefish_runtime/kernel.log");
695   std::regex finger_pattern(
696       "\\[\\s*[0-9]*\\.[0-9]+\\]\\s*GUEST_BUILD_FINGERPRINT:");
697   std::regex boot_pattern("VIRTUAL_DEVICE_BOOT_COMPLETED");
698   std::streampos last_pos;
699   bool first_iteration = true;
700   while (*interrupted == false) {
701     if (!FileExists(kernel_log_path)) {
702       LOG(ERROR) << kernel_log_path << " does not yet exist, so wait for 5s";
703       using namespace std::chrono_literals;
704       std::this_thread::sleep_for(5s);
705       continue;
706     }
707     std::ifstream kernel_log_file(kernel_log_path);
708     CF_EXPECT(kernel_log_file.is_open(),
709               "The kernel log file exists but it cannot be open.");
710     if (!first_iteration) {
711       kernel_log_file.seekg(last_pos);
712     } else {
713       first_iteration = false;
714       last_pos = kernel_log_file.tellg();
715     }
716     for (std::string line; std::getline(kernel_log_file, line);) {
717       last_pos = kernel_log_file.tellg();
718       // if the line broke before a newline, this will end up reading the
719       // previous line one more time but only with '\n'. That's okay
720       last_pos -= line.size();
721       if (last_pos != std::ios_base::beg) {
722         last_pos -= std::string("\n").size();
723       }
724       std::smatch matched;
725       if (std::regex_search(line, matched, finger_pattern)) {
726         std::string build_id = matched.suffix().str();
727         CF_EXPECT(instance_manager_.SetBuildId(uid, group_name, build_id));
728         continue;
729       }
730       if (std::regex_search(line, matched, boot_pattern)) {
731         return {};
732       }
733     }
734     using namespace std::chrono_literals;
735     std::this_thread::sleep_for(2s);
736   }
737   return CF_ERR("Cvd start kernel monitor interrupted.");
738 }
739 
HandleNoDaemon(const std::optional<selector::GroupCreationInfo> & group_creation_info,const uid_t uid)740 Result<cvd::Response> CvdStartCommandHandler::HandleNoDaemon(
741     const std::optional<selector::GroupCreationInfo>& group_creation_info,
742     const uid_t uid) {
743   std::atomic<bool> interrupted;
744   std::atomic<bool> worker_success;
745   interrupted = false;
746   worker_success = false;
747   const auto* group_info = std::addressof(*group_creation_info);
748   auto* interrupted_ptr = std::addressof(interrupted);
749   auto* worker_success_ptr = std::addressof(worker_success);
750   std::thread worker = std::thread(
751       [this, group_info, interrupted_ptr, worker_success_ptr, uid]() {
752         LOG(ERROR) << "worker thread started.";
753         auto result = HandleNoDaemonWorker(*group_info, interrupted_ptr, uid);
754         *worker_success_ptr = result.ok();
755         if (*worker_success_ptr == false) {
756           LOG(ERROR) << result.error().Trace();
757         }
758       });
759   auto infop = CF_EXPECT(subprocess_waiter_.Wait());
760   if (infop.si_code != CLD_EXITED || infop.si_status != EXIT_SUCCESS) {
761     // perhaps failed in launch
762     instance_manager_.RemoveInstanceGroup(uid, group_creation_info->home);
763     interrupted = true;
764   }
765   worker.join();
766   auto final_response = ResponseFromSiginfo(infop);
767   if (!final_response.has_status() ||
768       final_response.status().code() != cvd::Status::OK) {
769     return final_response;
770   }
771   // group_creation_info is nullopt only if is_help is false
772   return FillOutNewInstanceInfo(std::move(final_response),
773                                 *group_creation_info);
774 }
775 
HandleDaemon(std::optional<selector::GroupCreationInfo> & group_creation_info,const uid_t uid)776 Result<cvd::Response> CvdStartCommandHandler::HandleDaemon(
777     std::optional<selector::GroupCreationInfo>& group_creation_info,
778     const uid_t uid) {
779   auto infop = CF_EXPECT(subprocess_waiter_.Wait());
780   if (infop.si_code != CLD_EXITED || infop.si_status != EXIT_SUCCESS) {
781     instance_manager_.RemoveInstanceGroup(uid, group_creation_info->home);
782   }
783 
784   auto final_response = ResponseFromSiginfo(infop);
785   if (!final_response.has_status() ||
786       final_response.status().code() != cvd::Status::OK) {
787     return final_response;
788   }
789   MarkLockfilesInUse(*group_creation_info);
790 
791   auto set_build_id_result = SetBuildId(uid, group_creation_info->group_name,
792                                         group_creation_info->home);
793   if (!set_build_id_result.ok()) {
794     LOG(ERROR) << "Failed to set a build Id for "
795                << group_creation_info->group_name << " but will continue.";
796     LOG(ERROR) << "The error message was : "
797                << set_build_id_result.error().Trace();
798   }
799 
800   // group_creation_info is nullopt only if is_help is false
801   return FillOutNewInstanceInfo(std::move(final_response),
802                                 *group_creation_info);
803 }
804 
SetBuildId(const uid_t uid,const std::string & group_name,const std::string & home)805 Result<void> CvdStartCommandHandler::SetBuildId(const uid_t uid,
806                                                 const std::string& group_name,
807                                                 const std::string& home) {
808   // build id can't be found before this point
809   const auto build_id = CF_EXPECT(cvd_start_impl::ExtractBuildId(home));
810   CF_EXPECT(instance_manager_.SetBuildId(uid, group_name, build_id));
811   return {};
812 }
813 
Interrupt()814 Result<void> CvdStartCommandHandler::Interrupt() {
815   std::scoped_lock interrupt_lock(interruptible_);
816   interrupted_ = true;
817   if (!acloud_action_ended_) {
818     auto result = command_executor_.Interrupt();
819     if (!result.ok()) {
820       LOG(ERROR) << "Failed to interrupt CommandExecutor"
821                  << result.error().Message();
822     }
823   }
824   CF_EXPECT(subprocess_waiter_.Interrupt());
825   return {};
826 }
827 
FillOutNewInstanceInfo(cvd::Response && response,const selector::GroupCreationInfo & group_creation_info)828 Result<cvd::Response> CvdStartCommandHandler::FillOutNewInstanceInfo(
829     cvd::Response&& response,
830     const selector::GroupCreationInfo& group_creation_info) {
831   auto new_response = std::move(response);
832   auto& command_response = *(new_response.mutable_command_response());
833   auto& instance_group_info =
834       *(CF_EXPECT(command_response.mutable_instance_group_info()));
835   instance_group_info.set_group_name(group_creation_info.group_name);
836   instance_group_info.add_home_directories(group_creation_info.home);
837   for (const auto& per_instance_info : group_creation_info.instances) {
838     auto* new_entry = CF_EXPECT(instance_group_info.add_instances());
839     new_entry->set_name(per_instance_info.per_instance_name_);
840     new_entry->set_instance_id(per_instance_info.instance_id_);
841   }
842   return new_response;
843 }
844 
UpdateInstanceDatabase(const uid_t uid,const selector::GroupCreationInfo & group_creation_info)845 Result<void> CvdStartCommandHandler::UpdateInstanceDatabase(
846     const uid_t uid, const selector::GroupCreationInfo& group_creation_info) {
847   CF_EXPECT(instance_manager_.SetInstanceGroup(uid, group_creation_info),
848             group_creation_info.home
849                 << " is already taken so can't create new instance.");
850   return {};
851 }
852 
FireCommand(Command && command,const bool wait)853 Result<void> CvdStartCommandHandler::FireCommand(Command&& command,
854                                                  const bool wait) {
855   SubprocessOptions options;
856   if (!wait) {
857     options.ExitWithParent(false);
858   }
859   CF_EXPECT(subprocess_waiter_.Setup(command.Start(options)));
860   return {};
861 }
862 
HasHelpOpts(const std::vector<std::string> & args) const863 bool CvdStartCommandHandler::HasHelpOpts(
864     const std::vector<std::string>& args) const {
865   return IsHelpSubcmd(args);
866 }
867 
CmdList() const868 std::vector<std::string> CvdStartCommandHandler::CmdList() const {
869   std::vector<std::string> subcmd_list;
870   subcmd_list.reserve(supported_commands_.size());
871   for (const auto& cmd : supported_commands_) {
872     subcmd_list.emplace_back(cmd);
873   }
874   return subcmd_list;
875 }
876 
877 const std::array<std::string, 2> CvdStartCommandHandler::supported_commands_{
878     "start", "launch_cvd"};
879 
880 fruit::Component<fruit::Required<InstanceManager, HostToolTargetManager>>
CvdStartCommandComponent()881 CvdStartCommandComponent() {
882   return fruit::createComponent()
883       .addMultibinding<CvdServerHandler, CvdStartCommandHandler>();
884 }
885 
886 }  // namespace cuttlefish
887