#![cfg(unix)]
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use crate::error::{ComposeError, Result};
use crate::libpod::{urlencoded, API_PREFIX};
use super::terminal::{window_size, RawMode};
use super::Engine;
impl Engine {
pub(super) async fn exec_interactive(&self, exec_id: &str) -> Result<()> {
let start_path = format!("{API_PREFIX}/exec/{}/start", urlencoded(exec_id));
let body = br#"{"Detach":false,"Tty":true}"#;
let hijacked = self
.client
.post_hijack(&start_path, body)
.await
.map_err(ComposeError::Podman)?;
let _raw = RawMode::enable();
if let Some((rows, cols)) = window_size() {
self.resize_exec(exec_id, rows, cols).await;
}
let (mut server_read, mut server_write) = tokio::io::split(hijacked.stream);
let mut stdin = tokio::io::stdin();
let mut stdout = tokio::io::stdout();
let mut winch =
tokio::signal::unix::signal(tokio::signal::unix::SignalKind::window_change())
.map_err(ComposeError::Io)?;
let mut to_server = [0u8; 8 * 1024];
let mut from_server = [0u8; 32 * 1024];
loop {
tokio::select! {
read = server_read.read(&mut from_server) => {
match read {
Ok(0) => break,
Ok(n) => {
stdout.write_all(&from_server[..n]).await.map_err(ComposeError::Io)?;
stdout.flush().await.map_err(ComposeError::Io)?;
}
Err(_) => break,
}
}
read = stdin.read(&mut to_server) => {
match read {
Ok(0) => {
let _ = server_write.shutdown().await;
}
Ok(n) => {
if server_write.write_all(&to_server[..n]).await.is_err() {
break;
}
if server_write.flush().await.is_err() {
break;
}
}
Err(_) => break,
}
}
_ = winch.recv() => {
if let Some((rows, cols)) = window_size() {
self.resize_exec(exec_id, rows, cols).await;
}
}
}
}
Ok(())
}
async fn resize_exec(&self, exec_id: &str, rows: u16, cols: u16) {
let path = format!(
"{API_PREFIX}/exec/{}/resize?h={rows}&w={cols}",
urlencoded(exec_id),
);
if let Err(e) = self.client.post_empty_ok(&path).await {
tracing::debug!("exec resize to {rows}x{cols} failed: {e}");
}
}
}