1use crate::platform::{GuestConsole, PlatformServices, Stdio};
6use anyhow::{Error, anyhow};
7use fidl::endpoints::create_proxy;
8use fidl_fuchsia_virtualization::{GuestMarker, GuestProxy, GuestStatus};
9use fuchsia_async as fasync;
10use guest_cli_args as arguments;
11use std::fmt;
12
13#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
14pub enum AttachResult {
15 Attached,
16 NotRunning,
17 AttachFailure,
18}
19
20impl fmt::Display for AttachResult {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 match self {
23 AttachResult::Attached => {
24 write!(f, "Disconnected from guest after a successful attach")
25 }
26 AttachResult::NotRunning => write!(f, "Can't attach to a non-running guest"),
27 AttachResult::AttachFailure => write!(f, "Failed to attach to guest"),
28 }
29 }
30}
31
32pub async fn handle_attach<P: PlatformServices>(
33 services: &P,
34 args: &arguments::attach_args::AttachArgs,
35) -> Result<AttachResult, Error> {
36 let manager = services.connect_to_manager(args.guest_type).await?;
37 let status = manager.get_info().await?.guest_status.expect("guest status should always be set");
38 if status != GuestStatus::Starting && status != GuestStatus::Running {
39 return Ok(AttachResult::NotRunning);
40 }
41
42 let (guest_endpoint, guest_server_end) = create_proxy::<GuestMarker>();
43 manager
44 .connect(guest_server_end)
45 .await
46 .map_err(|err| anyhow!("failed to get a connect response: {}", err))?
47 .map_err(|err| anyhow!("connect failed with: {:?}", err))?;
48
49 Ok(match attach(guest_endpoint, args.serial).await {
50 Ok(()) => AttachResult::Attached,
51 Err(_) => AttachResult::AttachFailure,
52 })
53}
54
55pub async fn attach(guest: GuestProxy, serial_only: bool) -> Result<(), Error> {
56 if serial_only { attach_serial(guest).await } else { attach_console_and_serial(guest).await }
57}
58
59async fn attach_console_and_serial(guest: GuestProxy) -> Result<(), Error> {
62 let guest_serial_response = guest.get_serial().await?;
64 let guest_serial = fasync::Socket::from_socket(guest_serial_response);
65 let serial_output = async {
66 futures::io::copy(guest_serial, &mut GuestConsole::get_unblocked_stdio(Stdio::Stdout))
67 .await
68 .map(|_| ())
69 .map_err(anyhow::Error::from)
70 };
71
72 let console_input = guest.get_console().await?.map_err(|err| anyhow!(format!("{:?}", err)))?;
75 let console_output = guest.get_console().await?.map_err(|err| anyhow!(format!("{:?}", err)))?;
76 let guest_console = GuestConsole::new(console_input, console_output)?;
77
78 futures::future::try_join(serial_output, guest_console.run_with_stdio())
79 .await
80 .map(|_| ())
81 .map_err(anyhow::Error::from)
82}
83
84async fn attach_serial(guest: GuestProxy) -> Result<(), Error> {
86 let serial_input = guest.get_serial().await?;
89 let serial_output = guest.get_serial().await?;
90
91 let guest_console = GuestConsole::new(serial_input, serial_output)?;
92 guest_console.run_with_stdio().await
93}
94
95#[cfg(test)]
96mod test {
97 use super::*;
98 use fidl::Socket;
99 use fidl::endpoints::create_proxy_and_stream;
100 use fidl_fuchsia_virtualization::GuestError;
101 use futures::StreamExt;
102 use futures::future::join;
103
104 #[fuchsia::test(allow_stalls = false)]
105 async fn launch_invalid_console_returns_error() {
106 let (guest_proxy, mut guest_stream) = create_proxy_and_stream::<GuestMarker>();
107 let (serial_launch_sock, _serial_server_sock) = Socket::create_stream();
108
109 let server = async move {
110 let serial_responder = guest_stream
111 .next()
112 .await
113 .expect("Failed to read from stream")
114 .expect("Failed to parse request")
115 .into_get_serial()
116 .expect("Unexpected call to Guest Proxy");
117 serial_responder.send(serial_launch_sock).expect("Failed to send response to proxy");
118
119 let console_responder = guest_stream
120 .next()
121 .await
122 .expect("Failed to read from stream")
123 .expect("Failed to parse request")
124 .into_get_console()
125 .expect("Unexpected call to Guest Proxy");
126 console_responder
127 .send(Err(GuestError::DeviceNotPresent))
128 .expect("Failed to send response to proxy");
129 };
130
131 let client = attach(guest_proxy, false);
132 let (_, client_res) = join(server, client).await;
133 assert!(client_res.is_err());
134 }
135}