Prevent run_cvd abort and keep monitor socket alive on graceful VM sh… - #3154
SuperStrongDinosaur wants to merge 5 commits into
Conversation
e6e2de6 to
5a46719
Compare
| LOG(INFO) << "Process monitor has exited (guest VM shut down). Server " | ||
| "loop continuing to listen for status/restart."; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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:
- Graceful Shutdown: The
crosvmexits cleanly with code 0 . In this case,ProcessMonitorexits with code 0, andrun_cvdkeeps the launcher socket open to listen forcvd restart/status. - Unexpected Crash: A critical process exits with a signal (
WIFSIGNALED) or a non-zero exit code. In this case,ProcessMonitorexits with code 1, andrun_cvdreports 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?
27811cc to
30ae9a6
Compare
30ae9a6 to
80e7b24
Compare
80e7b24 to
597ece4
Compare
| return {}; | ||
| } | ||
|
|
||
| bool IsVmmCommand(const Command& cmd) { |
There was a problem hiding this comment.
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"?
There was a problem hiding this comment.
Done. Introduced ProcessCategory in command_source.h, stored it in MonitorCommand and MonitorEntry. Removed the IsVmmCommand name-matching helper completely.
| if (is_vmm && WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) { | ||
| LOG(INFO) | ||
| << "Stopping all monitored processes due to graceful exit " | ||
| "of critical process " | ||
| << name; |
There was a problem hiding this comment.
Does this mean that a graceful exit of the openWRT VM will trigger a shutdown of the android VM? That also seems undesirable.
There was a problem hiding this comment.
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.
| auto stop_result = process_monitor.StopMonitoredProcesses(); | ||
| if (!stop_result.has_value()) { | ||
| return CF_ERR( | ||
| "process monitor exited unexpectedly: " << stop_result.error()); | ||
| } |
There was a problem hiding this comment.
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());There was a problem hiding this comment.
Done. Simplified to CF_EXPECT(process_monitor.StopMonitoredProcesses()); to preserve the original error result and stack trace
| CF_EXPECT(device_status_.load() != DeviceStatus::kGuestOff, | ||
| "Device is powered off, cannot suspend"); |
There was a problem hiding this comment.
Done. Implemented format_as, operator<< and converted the precondition checks in HandleExtended to use CF_EXPECT_NE and CF_EXPECT_EQ.
| LOG(INFO) << "Process monitor has exited (guest VM shut down). Server " | ||
| "loop continuing to listen for status/restart."; |
94c2103 to
47388ab
Compare
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
• 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.
• 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.
• 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.
• 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