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;
16use term_model::ansi::Processor;
17use term_model::event::EventListener;
18
19const BYTE_BUFFER_MAX_SIZE: usize = 128;
20
21pub trait SessionManagerClient: 'static + Clone {
22 type Listener;
23
24 fn create_terminal(
25 &self,
26 id: u32,
27 title: String,
28 make_active: bool,
29 pty: ServerPty,
30 ) -> Result<Terminal<Self::Listener>, Error>;
31 fn request_update(&self, id: u32);
32}
33
34pub struct SessionManager {
35 keep_log_visible: bool,
36 first_session_id: u32,
37 next_session_id: Rc<RefCell<u32>>,
38 has_primary_connected: Rc<RefCell<bool>>,
39}
40
41impl SessionManager {
42 pub fn new(keep_log_visible: bool, first_session_id: u32) -> Self {
43 let next_session_id = Rc::new(RefCell::new(first_session_id));
44 let has_primary_connected = Rc::new(RefCell::new(false));
45
46 Self { keep_log_visible, first_session_id, next_session_id, has_primary_connected }
47 }
48
49 pub fn set_has_primary_connected(&mut self, has_primary_connected: bool) {
50 *self.has_primary_connected.borrow_mut() = has_primary_connected;
51 }
52
53 pub fn bind<T: SessionManagerClient>(&mut self, client: &T, channel: fasync::Channel)
54 where
55 <T as SessionManagerClient>::Listener: EventListener,
56 {
57 let keep_log_visible = self.keep_log_visible;
58 let first_session_id = self.first_session_id;
59 let next_session_id = Rc::clone(&self.next_session_id);
60 let has_primary_connected = Rc::clone(&self.has_primary_connected);
61 let client = client.clone();
62
63 fasync::Task::local(
64 async move {
65 let mut stream = SessionManagerRequestStream::from_channel(channel);
66 while let Some(request) = stream.try_next().await? {
67 match request {
68 SessionManagerRequest::CreateSession { session, control_handle: _ } => {
69 let id = {
70 let mut next_session_id = next_session_id.borrow_mut();
71 let id = *next_session_id;
72 *next_session_id += 1;
73 id
74 };
75 let make_active = !keep_log_visible && id == first_session_id;
76 let () = Self::create_session(session, &client, id, make_active).await;
77 }
78 SessionManagerRequest::HasPrimaryConnected { responder } => {
79 responder
80 .send(*has_primary_connected.borrow())
81 .context("error sending response")?;
82 }
83 }
84 }
85 Ok(())
86 }
87 .unwrap_or_else(|e: anyhow::Error| eprintln!("{:?}", e)),
88 )
89 .detach();
90 }
91
92 async fn create_session<T: SessionManagerClient>(
93 session: ServerEnd<DeviceMarker>,
94 client: &T,
95 id: u32,
96 make_active: bool,
97 ) where
98 <T as SessionManagerClient>::Listener: EventListener,
99 {
100 let client = client.clone();
101 let pty = ServerPty::new().expect("failed to create PTY");
102 let () = pty.open_client(session).await.expect("failed to connect session");
103 let read_fd = pty.try_clone_fd().expect("unable to clone PTY fd");
104 let mut write_fd = pty.try_clone_fd().expect("unable to clone PTY fd");
105 let terminal = client
106 .create_terminal(id, String::new(), make_active, pty)
107 .expect("failed to create terminal");
108 let term = terminal.clone_term();
109
110 fasync::Task::local(async move {
111 let mut evented_fd = unsafe {
112 fasync::net::EventedFd::new(read_fd).expect("failed to create evented_fd")
117 };
118
119 let mut parser = Processor::new();
120
121 let mut read_buf = [0u8; BYTE_BUFFER_MAX_SIZE];
122 loop {
123 let result = evented_fd.read(&mut read_buf).await;
124 let read_count = result.unwrap_or_else(|e: std::io::Error| {
125 println!("vc: failed to read bytes, dropping current message: {:?}", e);
126 0
127 });
128 let mut term = term.borrow_mut();
129 if read_count > 0 {
130 for byte in &read_buf[0..read_count] {
131 parser.advance(&mut *term, *byte, &mut write_fd);
132 }
133 client.request_update(id);
134 }
135 }
136 })
137 .detach()
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use crate::colors::ColorScheme;
145 use fuchsia_async as fasync;
146 use term_model::event::Event;
147
148 #[derive(Default)]
149 struct TestListener;
150
151 impl EventListener for TestListener {
152 fn send_event(&self, _event: Event) {}
153 }
154
155 #[derive(Default, Clone)]
156 struct TestSessionManagerClient;
157
158 impl SessionManagerClient for TestSessionManagerClient {
159 type Listener = TestListener;
160
161 fn create_terminal(
162 &self,
163 _id: u32,
164 title: String,
165 _make_active: bool,
166 pty: ServerPty,
167 ) -> Result<Terminal<Self::Listener>, Error> {
168 Ok(Terminal::new(
169 TestListener::default(),
170 title,
171 ColorScheme::default(),
172 1024,
173 Some(pty),
174 ))
175 }
176 fn request_update(&self, _id: u32) {}
177 }
178
179 #[fasync::run_singlethreaded(test)]
180 async fn can_create_session() -> Result<(), Error> {
181 let client = TestSessionManagerClient::default();
182 let (_, server_end) = fidl::endpoints::create_endpoints();
183 let () = SessionManager::create_session(server_end, &client, 0, false).await;
184 Ok(())
185 }
186}