2424#include < unistd.h>
2525
2626#include < algorithm>
27+ #include < chrono>
28+ #include < cstdio>
2729#include < cstdlib>
2830#include < cstring>
2931#include < iostream>
3234#include < optional>
3335#include < set>
3436#include < string>
37+ #include < thread>
3538#include < utility>
3639#include < vector>
3740
38- #include < fmt/core.h>
39- #include < fmt/format.h>
4041#include " absl/log/log.h"
42+ #include " absl/strings/match.h"
4143#include " absl/strings/str_join.h"
4244#include " absl/strings/str_split.h"
45+ #include " fmt/core.h"
46+ #include " fmt/format.h"
4347
48+ #include " cuttlefish/common/libs/fs/shared_buf.h"
4449#include " cuttlefish/common/libs/fs/shared_fd.h"
4550#include " cuttlefish/common/libs/utils/contains.h"
4651#include " cuttlefish/common/libs/utils/files.h"
47- #include " cuttlefish/flag_parser/flag.h"
48- #include " cuttlefish/flag_parser/gflags_compat.h"
4952#include " cuttlefish/common/libs/utils/json.h"
5053#include " cuttlefish/common/libs/utils/subprocess.h"
5154#include " cuttlefish/common/libs/utils/users.h"
55+ #include " cuttlefish/flag_parser/flag.h"
56+ #include " cuttlefish/flag_parser/gflags_compat.h"
5257#include " cuttlefish/host/commands/cvd/cli/command_request.h"
5358#include " cuttlefish/host/commands/cvd/cli/commands/command_handler.h"
5459#include " cuttlefish/host/commands/cvd/cli/commands/host_tool_target.h"
60+ #include " cuttlefish/host/commands/cvd/cli/commands/monitor/monitor.h"
5561#include " cuttlefish/host/commands/cvd/cli/help_format.h"
5662#include " cuttlefish/host/commands/cvd/cli/selector/selector.h"
5763#include " cuttlefish/host/commands/cvd/cli/types.h"
@@ -279,7 +285,8 @@ Result<Command> ConstructCvdNonHelpCommand(const std::string& bin_file,
279285 const LocalInstanceGroup& group,
280286 const cvd_common::Args& args,
281287 const cvd_common::Envs& envs,
282- const CommandRequest& request) {
288+ const CommandRequest& request,
289+ SharedFD output_fd = SharedFD()) {
283290 auto bin_path = group.HostArtifactsPath ();
284291 CF_EXPECTF (PotentiallyHostArtifactsPath (bin_path),
285292 " ANDROID_HOST_OUT, \" {}\" is not a tool directory" , bin_path);
@@ -292,10 +299,17 @@ Result<Command> ConstructCvdNonHelpCommand(const std::string& bin_file,
292299 .working_dir = CurrentDirectory (),
293300 .command_name = bin_file};
294301 Command non_help_command = CF_EXPECT (ConstructCommand (construct_cmd_param));
295- // Print everything to stderr, cvd needs to print JSON to stdout which
296- // would be unparseable with the subcommand's output.
297- non_help_command.RedirectStdIO (Subprocess::StdIOChannel::kStdOut ,
298- Subprocess::StdIOChannel::kStdErr );
302+ if (output_fd->IsOpen ()) {
303+ non_help_command.RedirectStdIO (Subprocess::StdIOChannel::kStdOut ,
304+ output_fd);
305+ non_help_command.RedirectStdIO (Subprocess::StdIOChannel::kStdErr ,
306+ output_fd);
307+ } else {
308+ // Print everything to stderr, cvd needs to print JSON to stdout which
309+ // would be unparseable with the subcommand's output.
310+ non_help_command.RedirectStdIO (Subprocess::StdIOChannel::kStdOut ,
311+ Subprocess::StdIOChannel::kStdErr );
312+ }
299313 return non_help_command;
300314}
301315
@@ -317,16 +331,11 @@ CvdStartCommandHandler::CvdStartCommandHandler(
317331 InstanceManager& instance_manager)
318332 : instance_manager_(instance_manager) {}
319333
320- static Result<void > ConsumeDaemonModeFlag (cvd_common::Args& args) {
334+ static Result<bool > ConsumeDaemonModeFlag (cvd_common::Args& args) {
321335 bool daemon = true ;
322- Flag flag =
323- GflagsCompatFlag (" daemon" , daemon)
324- .AddValidator ([&daemon]() -> Result<void > {
325- CF_EXPECT (!!daemon, " `cvd start` must always run in daemon mode." );
326- return {};
327- });
336+ Flag flag = GflagsCompatFlag (" daemon" , daemon);
328337 CF_EXPECT (ConsumeFlags ({flag}, args));
329- return {} ;
338+ return daemon ;
330339}
331340
332341static bool HasUnsafeFlagsForBypass (const std::vector<std::string>& args) {
@@ -370,8 +379,24 @@ Result<void> CvdStartCommandHandler::Handle(const CommandRequest& request) {
370379 }
371380 }
372381
373- CF_EXPECT (ConsumeDaemonModeFlag (subcmd_args));
382+ bool is_daemon = CF_EXPECT (ConsumeDaemonModeFlag (subcmd_args));
374383 subcmd_args.push_back (" --daemon=true" );
384+ if (!is_daemon) {
385+ bool has_stats_flag = false ;
386+ for (const auto & arg : subcmd_args) {
387+ if (absl::StartsWith (arg, " --report_anonymous_usage_stats" ) ||
388+ absl::StartsWith (arg, " -report_anonymous_usage_stats" )) {
389+ has_stats_flag = true ;
390+ break ;
391+ }
392+ }
393+ if (!has_stats_flag) {
394+ // If the user did not specify the anonymous usage stats flag, default it
395+ // to 'n' to ensure that cvd_internal_start does not block waiting for
396+ // input on stdin.
397+ subcmd_args.push_back (" --report_anonymous_usage_stats=n" );
398+ }
399+ }
375400
376401 LocalInstanceGroup group =
377402 CF_EXPECT (selector::SelectGroup (instance_manager_, request),
@@ -393,12 +418,25 @@ Result<void> CvdStartCommandHandler::Handle(const CommandRequest& request) {
393418 CF_EXPECT (HostPackageSubstitution (group.HostArtifactsPath (),
394419 own_flags_.host_substitutions ));
395420
396- Command command = CF_EXPECT (
397- ConstructCvdNonHelpCommand (bin, group, subcmd_args, envs, request));
421+ SharedFD memfd;
422+ SharedFD stop_eventfd;
423+ if (!is_daemon) {
424+ memfd = SharedFD::MemfdCreate (" cvd_internal_start_output" );
425+ CF_EXPECT (memfd->IsOpen (), " Failed to create memfd for subprocess output" );
426+
427+ stop_eventfd = SharedFD::Event ();
428+ CF_EXPECT (stop_eventfd->IsOpen (),
429+ " Failed to create eventfd for stopping monitor" );
430+ }
398431
399- // The instance database needs to be updated if an interrupt is received.
400- auto handle_res = PushInterruptListener ([this , &group](int signal) {
432+ Command command = CF_EXPECT (ConstructCvdNonHelpCommand (
433+ bin, group, subcmd_args, envs, request, memfd));
434+
435+ auto handle_res = PushInterruptListener ([this , &group, stop_eventfd](int signal) {
401436 LOG (WARNING ) << strsignal (signal) << " signal received, cleanning up" ;
437+ if (stop_eventfd->IsOpen ()) {
438+ stop_eventfd->EventfdWrite (1 );
439+ }
402440 auto interrupt_res = subprocess_waiter_.Interrupt ();
403441 if (!interrupt_res.ok ()) {
404442 LOG (ERROR ) << " Failed to stop subprocesses: " << interrupt_res.error ();
@@ -417,17 +455,71 @@ Result<void> CvdStartCommandHandler::Handle(const CommandRequest& request) {
417455
418456 std::abort ();
419457 });
458+
420459 auto listener_handle = CF_EXPECT (std::move (handle_res));
421460 group.SetAllStates (cvd::INSTANCE_STATE_STARTING );
422461 group.SetStartTime (CvdServerClock::now ());
423462 CF_EXPECT (instance_manager_.UpdateInstanceGroup (group));
424- CF_EXPECT (
425- LaunchDeviceInterruptible (std::move (command), group, envs, request));
463+
464+ LOG (INFO )
465+ << " By using this Android Virtual Device, you agree to Google Terms of "
466+ " Service (https://policies.google.com/terms). The Google Privacy "
467+ " Policy (https://policies.google.com/privacy) describes how Google "
468+ " handles information generated as you use Google services." ;
469+
470+ std::thread monitor_thread;
471+ Result<void > monitor_res;
472+
473+ if (!is_daemon) {
474+ monitor_thread = std::thread ([&group, stop_eventfd, &monitor_res]() {
475+ const auto & first_instance = *group.Instances ().begin ();
476+ monitor_res = MonitorLogs (first_instance, stop_eventfd);
477+ });
478+ }
479+
480+ auto launch_res =
481+ LaunchDeviceInterruptible (std::move (command), group, envs, request);
482+
483+ if (!launch_res.ok ()) {
484+ if (!is_daemon) {
485+ if (stop_eventfd->IsOpen ()) {
486+ stop_eventfd->EventfdWrite (1 );
487+ }
488+ if (monitor_thread.joinable ()) {
489+ monitor_thread.join ();
490+ }
491+ memfd->LSeek (0 , SEEK_SET );
492+ std::string full_output;
493+ ReadAll (memfd, &full_output);
494+ std::cout << full_output << std::flush;
495+ }
496+ return launch_res;
497+ }
498+
426499 group.SetAllStates (cvd::INSTANCE_STATE_RUNNING );
427500 CF_EXPECT (instance_manager_.UpdateInstanceGroup (group));
501+
502+ if (!is_daemon) {
503+ listener_handle.reset ();
504+ auto stop_listener =
505+ CF_EXPECT (PushInterruptListener ([stop_eventfd](int ) {
506+ stop_eventfd->EventfdWrite (1 );
507+ }));
508+
509+ if (monitor_thread.joinable ()) {
510+ monitor_thread.join ();
511+ }
512+ stop_listener.reset ();
513+
514+ LOG (INFO ) << " Stopping device..." ;
515+ CF_EXPECT (instance_manager_.StopInstanceGroup (
516+ group, std::chrono::seconds (5 ), InstanceDirActionOnStop::Keep, {}));
517+ LOG (INFO ) << " Device stopped." ;
518+ return monitor_res;
519+ }
520+
428521 listener_handle.reset ();
429522
430- // show user a more succinct output
431523 if (isatty (0 )) {
432524 std::vector<std::string> instance_names;
433525 for (const auto & instance : group.Instances ()) {
@@ -549,6 +641,12 @@ Result<void> CvdStartCommandHandler::LaunchSingleInstance(
549641 }
550642 };
551643
644+ LOG (INFO )
645+ << " By using this Android Virtual Device, you agree to Google Terms of "
646+ " Service (https://policies.google.com/terms). The Google Privacy "
647+ " Policy (https://policies.google.com/privacy) describes how Google "
648+ " handles information generated as you use Google services." ;
649+
552650 set_instance_state (cvd::INSTANCE_STATE_STARTING );
553651 group.SetStartTime (CvdServerClock::now ());
554652 CF_EXPECT (instance_manager_.UpdateInstanceGroup (group));
0 commit comments