1616
1717#include " cuttlefish/host/commands/cvd/cli/commands/start.h"
1818
19+ #include < fcntl.h>
1920#include < signal.h> // IWYU pragma: keep
2021#include < stddef.h>
2122#include < stdlib.h>
4041#include " absl/strings/str_join.h"
4142#include " absl/strings/str_split.h"
4243
44+ #include " cuttlefish/common/libs/fs/shared_fd.h"
4345#include " cuttlefish/common/libs/utils/contains.h"
4446#include " cuttlefish/common/libs/utils/files.h"
4547#include " cuttlefish/flag_parser/flag.h"
@@ -327,6 +329,21 @@ static Result<void> ConsumeDaemonModeFlag(cvd_common::Args& args) {
327329 return {};
328330}
329331
332+ static bool HasUnsafeFlagsForBypass (const std::vector<std::string>& args) {
333+ std::vector<std::string> args_copy = args;
334+ bool daemon = true ;
335+ std::string report_anonymous = " " ;
336+ std::vector<Flag> safe_flags = {
337+ GflagsCompatFlag (" daemon" , daemon),
338+ GflagsCompatFlag (" report_anonymous_usage_stats" , report_anonymous),
339+ };
340+ auto res = ConsumeFlags (safe_flags, args_copy);
341+ if (!res.ok ()) {
342+ return true ;
343+ }
344+ return !args_copy.empty ();
345+ }
346+
330347Result<void > CvdStartCommandHandler::Handle (const CommandRequest& request) {
331348 std::vector<std::string> subcmd_args = request.SubcommandArguments ();
332349 CF_EXPECT (!GetConfigPath (subcmd_args).has_value (),
@@ -337,6 +354,22 @@ Result<void> CvdStartCommandHandler::Handle(const CommandRequest& request) {
337354 return CF_ERR (NoGroupMessage (request));
338355 }
339356
357+ if (request.Selectors ().instance_names &&
358+ request.Selectors ().instance_names ->size () == 1 ) {
359+ auto [instance, group] =
360+ CF_EXPECT (selector::SelectInstance (instance_manager_, request));
361+
362+ if (instance.State () == cvd::INSTANCE_STATE_STOPPED &&
363+ group.StartTime () != TimeStamp{} &&
364+ !HasUnsafeFlagsForBypass (subcmd_args)) {
365+ CF_EXPECT (LaunchSingleInstance (instance, group, request));
366+ return {};
367+ } else {
368+ VLOG (1 ) << " Instance is not in stopped state. Proceeding with "
369+ " normal group start." ;
370+ }
371+ }
372+
340373 CF_EXPECT (ConsumeDaemonModeFlag (subcmd_args));
341374 subcmd_args.push_back (" --daemon=true" );
342375
@@ -472,6 +505,72 @@ Result<void> CvdStartCommandHandler::LaunchDeviceInterruptible(
472505 return {};
473506}
474507
508+ Result<void > CvdStartCommandHandler::LaunchSingleInstance (
509+ LocalInstance& instance, LocalInstanceGroup& group,
510+ const CommandRequest& request) {
511+ auto bin_path = group.HostArtifactsPath () + " /bin/run_cvd" ;
512+ cvd_common::Envs run_cvd_envs = request.Env ();
513+ run_cvd_envs[kCuttlefishInstanceEnvVarName ] = std::to_string (instance.Id ());
514+ run_cvd_envs[" HOME" ] = group.HomeDir ();
515+ run_cvd_envs[kAndroidHostOut ] = group.HostArtifactsPath ();
516+ run_cvd_envs[kAndroidProductOut ] = group.ProductOutPath ();
517+ run_cvd_envs[kAndroidSoongHostOut ] = group.HostArtifactsPath ();
518+ run_cvd_envs[kCvdMarkEnv ] = " true" ;
519+
520+ ConstructCommandParam construct_cmd_param{.bin_path = bin_path,
521+ .home = group.HomeDir (),
522+ .args = cvd_common::Args{},
523+ .envs = run_cvd_envs,
524+ .working_dir = CurrentDirectory (),
525+ .command_name = " run_cvd" };
526+
527+ Command command = CF_EXPECT (ConstructCommand (construct_cmd_param));
528+ command.RedirectStdIO (Subprocess::StdIOChannel::kStdOut ,
529+ Subprocess::StdIOChannel::kStdErr );
530+ SharedFD dev_null = SharedFD::Open (" /dev/null" , O_RDONLY );
531+ if (dev_null->IsOpen ()) {
532+ command.RedirectStdIO (Subprocess::StdIOChannel::kStdIn , dev_null);
533+ } else {
534+ LOG (ERROR ) << " Failed to open /dev/null: " << dev_null->StrError ();
535+ }
536+
537+ auto symlink_config_res = SymlinkPreviousConfig (group.HomeDir ());
538+ if (!symlink_config_res.ok ()) {
539+ LOG (ERROR ) << " Failed to symlink the config file at system wide home: "
540+ << symlink_config_res.error ();
541+ }
542+
543+ auto set_instance_state = [&group, &instance](cvd::InstanceState state) {
544+ for (auto & inst : group.Instances ()) {
545+ if (inst.Id () == instance.Id ()) {
546+ inst.SetState (state);
547+ break ;
548+ }
549+ }
550+ };
551+
552+ set_instance_state (cvd::INSTANCE_STATE_STARTING );
553+ group.SetStartTime (CvdServerClock::now ());
554+ CF_EXPECT (instance_manager_.UpdateInstanceGroup (group));
555+
556+ Result<void > start_res =
557+ LaunchDevice (std::move (command), group, run_cvd_envs, request);
558+
559+ if (!start_res.ok ()) {
560+ set_instance_state (cvd::INSTANCE_STATE_BOOT_FAILED );
561+ CF_EXPECT (instance_manager_.UpdateInstanceGroup (group));
562+ return start_res;
563+ }
564+
565+ set_instance_state (cvd::INSTANCE_STATE_RUNNING );
566+ CF_EXPECT (instance_manager_.UpdateInstanceGroup (group));
567+
568+ auto group_json = CF_EXPECT (group.FetchStatus ());
569+ std::cout << group_json.toStyledString ();
570+
571+ return {};
572+ }
573+
475574std::vector<HelpParagraph> CvdStartCommandHandler::Description () const {
476575 std::vector<HelpParagraph> description;
477576 description.emplace_back (
0 commit comments