Skip to content

Prevent run_cvd abort and keep monitor socket alive on graceful VM sh… - #3154

Open
SuperStrongDinosaur wants to merge 5 commits into
google:mainfrom
SuperStrongDinosaur:FixCuttlefishLauncherCrashOnGracefulGuestVMShutdown2
Open

SuperStrongDinosaur wants to merge 5 commits into
google:mainfrom
SuperStrongDinosaur:FixCuttlefishLauncherCrashOnGracefulGuestVMShutdown2

Conversation

@SuperStrongDinosaur

@SuperStrongDinosaur SuperStrongDinosaur commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes b/534717429: "Shutdown/Power on VMs does not work on cuttlefish"

Previously, when an Android guest VM initiated a clean shutdown the virtual machine manager exited with status code 0. ProcessMonitor treated any termination of a critical process as an error, causing run_cvd to abort with "process monitor has died". This caused cvd fleet and cvd status to report the device as Unreachable, and prevented subsequent cvd restart commands from reconnecting and booting the instance.

This PR enables run_cvd to differentiate graceful guest VM shutdown from unexpected process crashes, keep the launcher monitor socket active and listening in a powered-off standby state, and allow cvd restart to cleanly boot the instance back up.

Key Changes

  1. Differentiate Graceful VMM Exit from Process Failures

• process_monitor.cc: In MonitorLoop(), check the exit status of the VMM process. If a critical VMM process exits with WIFEXITED and status 0, log the clean shutdown and stop all other monitored subprocesses gracefully without returning an error.
• If a critical process exits due to a signal, with a non-zero exit code, or is an unexpected non-VMM daemon exiting prematurely, MonitorLoop() continues to treat it as an error and reports the failure.
• In MonitorRoutine(), monitor socket communication disconnects between parent and child during shutdown are logged as warnings rather than failing an otherwise clean shutdown.

  1. Precise VMM Process Identification & Auxiliary VM Handling

• Added IsVmmCommand() to accurately inspect whether a process is the main virtual machine manager.
• When binaries are wrapped by process_restarter, inspect the full command line to identify the underlying executable, preventing non-VMM daemons from matching.
• Explicitly exclude auxiliary VMs so that independent exits of auxiliary components do not trigger guest VM shutdown handling.

  1. Signal Safety & Clean Subprocess Termination

• Resume Suspended Subprocesses: In StopSubprocesses(), broadcast SIGCONT to process groups before executing process stoppers. Paused processes cannot otherwise process termination signals or exit hooks, which previously caused cvd restart to hang on suspended devices.
• Signal Interruption Protection: Wrapped both non-blocking and blocking waitpid() calls in ProcessMonitor::StopMonitoredProcesses() with TEMP_FAILURE_RETRY to protect against EINTR caused
by incoming signals like SIGCHLD.
• SIGPIPE Isolation: In Command::Start(), explicitly reset signal after fork() to ensure child subprocesses do not inherit run_cvd's SIG_IGN disposition across execvpe().
• Null Safety: Added explicit null checks in StopSubprocesses() to avoid dereferencing null subprocess pointers.

  1. Device Status Management & Action Guarding

• server_loop_impl.h / server_loop_impl.cpp:
• Added DeviceStatus::kGuestOff to track when the guest VM has powered off cleanly while run_cvd remains alive.
• In ServerLoopImpl::Run(), transition device_status_ to kGuestOff upon graceful monitor exit.
• Guarded extended launcher actions: reject invalid operations when the device is not active or is powered off.
• Documented LauncherAction::kStatus behavior and protocol trade-offs regarding cvd status reporting.

Verification

• Verified lifecycle workflows:
• Graceful guest VM shutdown via VePSM / adb reboot -p leaves launcher_monitor.sock active and eliminates zombie processes.
• cvd restart successfully connects, reaps state, and re-executes run_cvd.
• cvd stop cleanly terminates run_cvd with exit code 0.
• cvd status and cvd fleet successfully respond without connection errors.

Bug: b/534717429

@SuperStrongDinosaur
SuperStrongDinosaur force-pushed the FixCuttlefishLauncherCrashOnGracefulGuestVMShutdown2 branch 3 times, most recently from e6e2de6 to 5a46719 Compare September 10, 2026 09:21
@SuperStrongDinosaur SuperStrongDinosaur added the kokoro:run Run e2e tests. label Sep 10, 2026
@GoogleCuttlefishTesterBot GoogleCuttlefishTesterBot removed the kokoro:run Run e2e tests. label Sep 10, 2026
@SuperStrongDinosaur
SuperStrongDinosaur marked this pull request as ready for review September 10, 2026 15:47
Comment on lines +131 to +132
LOG(INFO) << "Process monitor has exited (guest VM shut down). Server "
"loop continuing to listen for status/restart.";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this assume that every process monitor exit implies a graceful VM shutdown? There's also the scenario that crosvm or another critical process exits unexpectedly and an error should be reported.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Yes, right now, run_cvd assumes any process monitor termination is a graceful shutdown, which would mask an unexpected crash of crosvm or something else.

We can differentiate between the two cases using the exit status of the monitor process:

  1. Graceful Shutdown: The crosvm exits cleanly with code 0 . In this case, ProcessMonitor exits with code 0, and run_cvd keeps the launcher socket open to listen for cvd restart/status.
  2. Unexpected Crash: A critical process exits with a signal (WIFSIGNALED) or a non-zero exit code. In this case, ProcessMonitor exits with code 1, and run_cvd reports an error .

Would you prefer updating ProcessMonitor to propagate this exit status so run_cvd can abort on unexpected crashes and only stay alive on 0 exits?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.

@SuperStrongDinosaur
SuperStrongDinosaur force-pushed the FixCuttlefishLauncherCrashOnGracefulGuestVMShutdown2 branch 3 times, most recently from 27811cc to 30ae9a6 Compare September 17, 2026 11:32
@SuperStrongDinosaur SuperStrongDinosaur added the kokoro:run Run e2e tests. label Sep 17, 2026
@GoogleCuttlefishTesterBot GoogleCuttlefishTesterBot removed the kokoro:run Run e2e tests. label Sep 17, 2026
@SuperStrongDinosaur
SuperStrongDinosaur force-pushed the FixCuttlefishLauncherCrashOnGracefulGuestVMShutdown2 branch from 30ae9a6 to 80e7b24 Compare September 17, 2026 12:29
@SuperStrongDinosaur SuperStrongDinosaur added the kokoro:run Run e2e tests. label Sep 17, 2026
@GoogleCuttlefishTesterBot GoogleCuttlefishTesterBot removed the kokoro:run Run e2e tests. label Sep 17, 2026
@SuperStrongDinosaur
SuperStrongDinosaur force-pushed the FixCuttlefishLauncherCrashOnGracefulGuestVMShutdown2 branch from 80e7b24 to 597ece4 Compare September 17, 2026 14:58
@SuperStrongDinosaur SuperStrongDinosaur added the kokoro:run Run e2e tests. label Sep 17, 2026
@GoogleCuttlefishTesterBot GoogleCuttlefishTesterBot removed the kokoro:run Run e2e tests. label Sep 17, 2026
return {};
}

bool IsVmmCommand(const Command& cmd) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than match on the name, this should be tracked the same way is_critical is tracked now. Maybe processes can be categorized as "vmm", "critical support", and "non-critical support"?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Introduced ProcessCategory in command_source.h, stored it in MonitorCommand and MonitorEntry. Removed the IsVmmCommand name-matching helper completely.

Comment on lines +169 to +173
if (is_vmm && WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) {
LOG(INFO)
<< "Stopping all monitored processes due to graceful exit "
"of critical process "
<< name;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that a graceful exit of the openWRT VM will trigger a shutdown of the android VM? That also seems undesirable.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. With ProcessCategory, auxiliary VMs like OpenWrt are configured as kCriticalSupport rather than kVmm. Only a clean exit of the primary kVmm process initiates the graceful VM shutdown sequence; an exit of OpenWrt will not trigger a guest VM shutdown.

Comment on lines +132 to +136
auto stop_result = process_monitor.StopMonitoredProcesses();
if (!stop_result.has_value()) {
return CF_ERR(
"process monitor exited unexpectedly: " << stop_result.error());
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is taking an error Result, converting it to a string, and packaging that inside another error Result. It would be simpler and better report the stack trace to run

CF_EXPECT(process_monitor.StopMonitoredProcesses());

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Simplified to CF_EXPECT(process_monitor.StopMonitoredProcesses()); to preserve the original error result and stack trace

Comment on lines +199 to +200
CF_EXPECT(device_status_.load() != DeviceStatus::kGuestOff,
"Device is powered off, cannot suspend");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and below, there is CF_EXPECT_NE and CF_EXPECT_EQ. It may require that DeviceStatus implements a format_as function (example) so that it can be rendered correctly for the comparison.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Implemented format_as, operator<< and converted the precondition checks in HandleExtended to use CF_EXPECT_NE and CF_EXPECT_EQ.

Comment on lines +131 to +132
LOG(INFO) << "Process monitor has exited (guest VM shut down). Server "
"loop continuing to listen for status/restart.";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.

@SuperStrongDinosaur
SuperStrongDinosaur force-pushed the FixCuttlefishLauncherCrashOnGracefulGuestVMShutdown2 branch from 94c2103 to 47388ab Compare September 21, 2026 11:51
@SuperStrongDinosaur SuperStrongDinosaur added kokoro:run Run e2e tests. and removed kokoro:run Run e2e tests. labels Sep 21, 2026
@GoogleCuttlefishTesterBot GoogleCuttlefishTesterBot removed the kokoro:run Run e2e tests. label Sep 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants