Enable --extended-status for crosvm and return reason why VM died.

This will allow us to detect guest VM kernel panics because they can be
configured to reboot on panic.

Bug: 211704107
Test: Ran some VMs manually with vm tool
Change-Id: I3845bb9d569ad0dc098013b527b69b31352e7e08
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index 0b1429c..76f4f47 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -21,19 +21,24 @@
 use log::{debug, error, info};
 use shared_child::SharedChild;
 use std::fs::{remove_dir_all, File};
+use std::io;
 use std::mem;
 use std::num::NonZeroU32;
 use std::os::unix::io::{AsRawFd, RawFd};
 use std::path::PathBuf;
-use std::process::Command;
+use std::process::{Command, ExitStatus};
 use std::sync::{Arc, Mutex};
 use std::thread;
 use vsock::VsockStream;
+use android_system_virtualizationservice::aidl::android::system::virtualizationservice::DeathReason::DeathReason;
 use android_system_virtualmachineservice::binder::Strong;
 use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
 
 const CROSVM_PATH: &str = "/apex/com.android.virt/bin/crosvm";
 
+/// The exit status which crosvm returns when a VM requests a reboot.
+const CROSVM_REBOOT_STATUS: i32 = 32;
+
 /// Configuration for a VM to run with crosvm.
 #[derive(Debug)]
 pub struct CrosvmConfig {
@@ -182,7 +187,8 @@
     /// This takes a separate reference to the `SharedChild` rather than using the one in
     /// `self.vm_state` to avoid holding the lock on `vm_state` while it is running.
     fn monitor(&self, child: Arc<SharedChild>) {
-        match child.wait() {
+        let result = child.wait();
+        match &result {
             Err(e) => error!("Error waiting for crosvm({}) instance to die: {}", child.id(), e),
             Ok(status) => info!("crosvm({}) exited with status {}", child.id(), status),
         }
@@ -192,7 +198,7 @@
         // Ensure that the mutex is released before calling the callbacks.
         drop(vm_state);
 
-        self.callbacks.callback_on_died(self.cid);
+        self.callbacks.callback_on_died(self.cid, death_reason(&result));
 
         // Delete temporary files.
         if let Err(e) = remove_dir_all(&self.temporary_directory) {
@@ -232,13 +238,31 @@
     }
 }
 
+fn death_reason(result: &Result<ExitStatus, io::Error>) -> DeathReason {
+    if let Ok(status) = result {
+        match status.code() {
+            None => DeathReason::KILLED,
+            Some(0) => DeathReason::SHUTDOWN,
+            Some(CROSVM_REBOOT_STATUS) => DeathReason::REBOOT,
+            Some(_) => DeathReason::UNKNOWN,
+        }
+    } else {
+        DeathReason::INFRASTRUCTURE_ERROR
+    }
+}
+
 /// Starts an instance of `crosvm` to manage a new VM.
 fn run_vm(config: CrosvmConfig) -> Result<SharedChild, Error> {
     validate_config(&config)?;
 
     let mut command = Command::new(CROSVM_PATH);
     // TODO(qwandor): Remove --disable-sandbox.
-    command.arg("run").arg("--disable-sandbox").arg("--cid").arg(config.cid.to_string());
+    command
+        .arg("--extended-status")
+        .arg("run")
+        .arg("--disable-sandbox")
+        .arg("--cid")
+        .arg(config.cid.to_string());
 
     if config.protected {
         command.arg("--protected-vm");