Gracefully stop the vm

Bug: 381815559
Test: click 'close' in the notification
Change-Id: Ic000bb7e62b5e5ec39b3c89b9cd9a05aee17588b
diff --git a/guest/shutdown_runner/.gitignore b/guest/shutdown_runner/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/guest/shutdown_runner/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/guest/shutdown_runner/Cargo.toml b/guest/shutdown_runner/Cargo.toml
new file mode 100644
index 0000000..b74e7ee
--- /dev/null
+++ b/guest/shutdown_runner/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "shutdown_runner"
+version = "0.1.0"
+edition = "2021"
+license = "Apache-2.0"
+
+[dependencies]
+anyhow = "1.0.94"
+clap = { version = "4.5.20", features = ["derive"] }
+log = "0.4.22"
+netdev = "0.31.0"
+prost = "0.13.3"
+tokio = { version = "1.40.0", features = ["rt-multi-thread"] }
+tonic = "0.12.3"
+
+[build-dependencies]
+tonic-build = "0.12.3"
diff --git a/guest/shutdown_runner/build.rs b/guest/shutdown_runner/build.rs
new file mode 100644
index 0000000..e3939d4
--- /dev/null
+++ b/guest/shutdown_runner/build.rs
@@ -0,0 +1,7 @@
+fn main() -> Result<(), Box<dyn std::error::Error>> {
+    let proto_file = "../../libs/debian_service/proto/DebianService.proto";
+
+    tonic_build::compile_protos(proto_file).unwrap();
+
+    Ok(())
+}
diff --git a/guest/shutdown_runner/src/main.rs b/guest/shutdown_runner/src/main.rs
new file mode 100644
index 0000000..19e9883
--- /dev/null
+++ b/guest/shutdown_runner/src/main.rs
@@ -0,0 +1,46 @@
+use api::debian_service_client::DebianServiceClient;
+use api::ShutdownQueueOpeningRequest;
+use std::process::Command;
+
+use anyhow::anyhow;
+use clap::Parser;
+use log::debug;
+pub mod api {
+    tonic::include_proto!("com.android.virtualization.terminal.proto");
+}
+
+#[derive(Parser)]
+/// Flags for running command
+pub struct Args {
+    /// grpc port number
+    #[arg(long)]
+    #[arg(alias = "grpc_port")]
+    grpc_port: String,
+}
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    let args = Args::parse();
+    let gateway_ip_addr = netdev::get_default_gateway()?.ipv4[0];
+
+    let server_addr = format!("http://{}:{}", gateway_ip_addr.to_string(), args.grpc_port);
+
+    debug!("connect to grpc server {}", server_addr);
+
+    let mut client = DebianServiceClient::connect(server_addr).await.map_err(|e| e.to_string())?;
+
+    let mut res_stream = client
+        .open_shutdown_request_queue(tonic::Request::new(ShutdownQueueOpeningRequest {}))
+        .await?
+        .into_inner();
+
+    while let Some(_response) = res_stream.message().await? {
+        let status = Command::new("poweroff").status().expect("power off");
+        if !status.success() {
+            return Err(anyhow!("Failed to power off: {status}").into());
+        }
+        debug!("poweroff");
+        break;
+    }
+    Ok(())
+}