use std::{os::fd::AsRawFd, sync::atomic::Ordering}; use anyhow::Result; use log::info; use sesh_proto::{sesh_attach_request, SeshAttachResponse, WinSize}; use sesh_shared::term::Size; use crate::{Seshd, Session}; use super::CommandResponse; impl Seshd { /// RPC handler for attaching to a session pub async fn exec_attach( &self, session: Option, size: Option, ) -> Result { if let Some(session) = session { let session = match &session { sesh_proto::sesh_attach_request::Session::Name(name) => self.sessions.get(name), sesh_proto::sesh_attach_request::Session::Id(id) => { self.sessions.iter().find_map(|entry| { if entry.value().id == *id as usize { Some(self.sessions.get(entry.key())).flatten() } else { None } }) } } .ok_or_else(|| anyhow::anyhow!("Session {} not found", session))?; if session.info.connected().load(Ordering::Relaxed) { return Err(anyhow::anyhow!("Session already connected")); } info!(target: &session.log_group(), "Attaching"); let size = if let Some(size) = size { Size { rows: size.rows as u16, cols: size.cols as u16, } } else { Size::term_size()? }; session.pty.resize(&Size { cols: size.cols.checked_sub(2).unwrap_or(2), rows: size.rows.checked_sub(2).unwrap_or(2), })?; tokio::task::spawn({ let sock_path = session.info.sock_path().clone(); let socket = session.listener.clone(); let file = session.pty.file().as_raw_fd(); let file = unsafe { libc::fcntl(file, libc::F_DUPFD, file) }; let connected = session.info.connected(); let attach_time = session.info.attach_time.clone(); async move { Session::start(sock_path, socket, file, connected, size, attach_time).await?; Result::<_, anyhow::Error>::Ok(()) } }); Ok(CommandResponse::AttachSession(SeshAttachResponse { socket: session.info.sock_path().to_string_lossy().to_string(), pid: session.pid(), name: session.name.clone(), program: session.program.clone(), })) } else { anyhow::bail!("No session specified"); } } }