virtual_console_lib/
session_manager.rs1use crate::terminal::Terminal;
6use anyhow::{Context as _, Error};
7use fidl::endpoints::{RequestStream, ServerEnd};
8use fidl_fuchsia_hardware_pty::DeviceMarker;
9use fidl_fuchsia_virtualconsole::{SessionManagerRequest, SessionManagerRequestStream};
10use fuchsia_async as fasync;
11use futures::io::AsyncReadExt;
12use futures::prelude::*;
13use pty::ServerPty;
14use std::cell::RefCell;
15use std::rc::Rc;
16
17const BYTE_BUFFER_MAX_SIZE: usize = 128;
18
19pub trait SessionManagerClient: 'static + Clone {
20 fn create_terminal(
21 &self,
22 id: u32,
23 title: String,
24 make_active: bool,
25 pty: ServerPty,
26 ) -> Result<Terminal, Error>;
27 fn request_update(&self, id: u32);
28}
29
30pub struct SessionManager {
31 keep_log_visible: bool,
32 first_session_id: u32,
33 next_session_id: Rc<RefCell<u32>>,
34 has_primary_connected: Rc<RefCell<bool>>,
35}
36
37impl SessionManager {
38 pub fn new(keep_log_visible: bool, first_session_id: u32) -> Self {
39 let next_session_id = Rc::new(RefCell::new(first_session_id));
40 let has_primary_connected = Rc::new(RefCell::new(false));
41
42 Self { keep_log_visible, first_session_id, next_session_id, has_primary_connected }
43 }
44
45 pub fn set_has_primary_connected(&mut self, has_primary_connected: bool) {
46 *self.has_primary_connected.borrow_mut() = has_primary_connected;
47 }
48
49 pub fn bind<T: SessionManagerClient>(&mut self, client: &T, channel: fasync::Channel) {
50 let keep_log_visible = self.keep_log_visible;
51 let first_session_id = self.first_session_id;
52 let next_session_id = Rc::clone(&self.next_session_id);
53 let has_primary_connected = Rc::clone(&self.has_primary_connected);
54 let client = client.clone();
55
56 fasync::Task::local(
57 async move {
58 let mut stream = SessionManagerRequestStream::from_channel(channel);
59 while let Some(request) = stream.try_next().await? {
60 match request {
61 SessionManagerRequest::CreateSession { session, control_handle: _ } => {
62 let id = {
63 let mut next_session_id = next_session_id.borrow_mut();
64 let id = *next_session_id;
65 *next_session_id += 1;
66 id
67 };
68 let make_active = !keep_log_visible && id == first_session_id;
69 let () = Self::create_session(session, &client, id, make_active).await;
70 }
71 SessionManagerRequest::HasPrimaryConnected { responder } => {
72 responder
73 .send(*has_primary_connected.borrow())
74 .context("error sending response")?;
75 }
76 }
77 }
78 Ok(())
79 }
80 .unwrap_or_else(|e: anyhow::Error| eprintln!("{:?}", e)),
81 )
82 .detach();
83 }
84
85 async fn create_session<T: SessionManagerClient>(
86 session: ServerEnd<DeviceMarker>,
87 client: &T,
88 id: u32,
89 make_active: bool,
90 ) {
91 let client = client.clone();
92 let pty = ServerPty::new().expect("failed to create PTY");
93 let () = pty.open_client(session).await.expect("failed to connect session");
94 let read_fd = pty.try_clone_fd().expect("unable to clone PTY fd");
95 let terminal = client
96 .create_terminal(id, String::new(), make_active, pty)
97 .expect("failed to create terminal");
98 let term = terminal.clone_term();
99
100 fasync::Task::local(async move {
101 let mut evented_fd = unsafe {
102 fasync::net::EventedFd::new(read_fd).expect("failed to create evented_fd")
107 };
108
109 let mut read_buf = [0u8; BYTE_BUFFER_MAX_SIZE];
110 loop {
111 let result = evented_fd.read(&mut read_buf).await;
112 let read_count = result.unwrap_or_else(|e: std::io::Error| {
113 println!("vc: failed to read bytes, dropping current message: {:?}", e);
114 0
115 });
116 if read_count > 0 {
117 let mut parser = term.borrow_mut();
118 for byte in &read_buf[0..read_count] {
119 parser.process(&[*byte]);
120 }
121 client.request_update(id);
122 }
123 }
124 })
125 .detach()
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132 use crate::colors::ColorScheme;
133 use fuchsia_async as fasync;
134
135 #[derive(Default, Clone)]
136 struct TestSessionManagerClient;
137
138 impl SessionManagerClient for TestSessionManagerClient {
139 fn create_terminal(
140 &self,
141 _id: u32,
142 title: String,
143 _make_active: bool,
144 pty: ServerPty,
145 ) -> Result<Terminal, Error> {
146 Ok(Terminal::new(title, ColorScheme::default(), 1024, Some(pty)))
147 }
148 fn request_update(&self, _id: u32) {}
149 }
150
151 #[fasync::run_singlethreaded(test)]
152 async fn can_create_session() -> Result<(), Error> {
153 let client = TestSessionManagerClient::default();
154 let (_, server_end) = fidl::endpoints::create_endpoints();
155 let () = SessionManager::create_session(server_end, &client, 0, false).await;
156 Ok(())
157 }
158}