提交 ad4c77ba authored 作者: Serhij S's avatar Serhij S

advanced terminal commands

上级 1d445817
......@@ -915,7 +915,7 @@ dependencies = [
[[package]]
name = "roboplc-cli"
version = "0.5.1"
version = "0.6.0"
dependencies = [
"ansi_term",
"bma-ts",
......@@ -1117,6 +1117,15 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.9"
......@@ -1262,6 +1271,7 @@ dependencies = [
"libc",
"mio",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"windows-sys 0.48.0",
]
......
[package]
name = "roboplc-cli"
version = "0.5.1"
version = "0.6.0"
edition = "2021"
authors = ["Serhij S. <div@altertech.com>"]
license = "Apache-2.0"
......@@ -26,7 +26,7 @@ ureq = { version = "2.9.6", features = ["json", "native-certs", "native-tls"] }
ureq_multipart = "1.1.1"
which = "3"
term_size = "0.3.2"
tokio = { version = "=1.36", features = ["rt", "fs"] }
tokio = { version = "=1.36", features = ["rt", "fs", "signal", "sync"] }
tokio-tungstenite = { version = "0.23.1", features = ["rustls"] }
once_cell = "1.19.0"
bma-ts = { version = "0.1.14", features = ["serde", "chrono"] }
......
use std::path::Path;
use std::sync::Arc;
use std::{collections::BTreeMap, io::Write as _};
use colored::Colorize;
......@@ -7,8 +8,17 @@ use tokio::io::AsyncReadExt as _;
use futures_util::{SinkExt, StreamExt};
use serde::{Deserialize, Serialize};
use tokio::signal::unix::SignalKind;
use tokio::sync::Mutex;
use tokio_tungstenite::tungstenite::Message;
#[derive(Serialize)]
#[serde(rename_all = "lowercase")]
enum Input {
Resize((usize, usize)),
Terminate,
}
pub fn exec(
url: &str,
key: &str,
......@@ -97,30 +107,83 @@ async fn exec_remote(
let mut stdout = std::io::stdout();
let mut stderr = std::io::stderr();
#[allow(unused_mut)]
let (mut sender, mut receiver) = socket.split();
#[cfg(target_os = "windows")]
let _ = sender;
let (sender, mut receiver) = socket.split();
let sender = Arc::new(Mutex::new(sender));
// input on windows is currently not supported
#[cfg(not(target_os = "windows"))]
let input_fut = tokio::spawn(async move {
let stdin = std::os::fd::AsRawFd::as_raw_fd(&std::io::stdin().lock());
let mut termios =
termios::Termios::from_fd(stdin).expect("Failed to get termios for stdin");
termios.c_lflag &= !(termios::ICANON | termios::ECHO);
termios::tcsetattr(stdin, termios::TCSANOW, &termios)
.expect("Failed to set termios for stdin");
let mut f = unsafe { <tokio::fs::File as std::os::fd::FromRawFd>::from_raw_fd(stdin) };
let buf = &mut [0u8; 4096];
while let Ok(b) = f.read(buf).await {
if b == 0 {
break;
}
if let Err(e) = sender.send(Message::Binary(buf[..b].to_vec())).await {
eprintln!("Error sending input: {}", e);
break;
let input_fut = {
let sender_c = sender.clone();
tokio::spawn(async move {
let stdin = std::os::fd::AsRawFd::as_raw_fd(&std::io::stdin().lock());
let mut termios =
termios::Termios::from_fd(stdin).expect("Failed to get termios for stdin");
termios.c_lflag &= !(termios::ICANON | termios::ECHO);
termios::tcsetattr(stdin, termios::TCSANOW, &termios)
.expect("Failed to set termios for stdin");
let mut f = unsafe { <tokio::fs::File as std::os::fd::FromRawFd>::from_raw_fd(stdin) };
let buf = &mut [0u8; 4096];
while let Ok(b) = f.read(buf).await {
if b == 0 {
break;
}
if let Err(e) = sender_c
.lock()
.await
.send(Message::Binary(buf[..b].to_vec()))
.await
{
eprintln!("Error sending input: {}", e);
break;
}
}
})
};
// signal handler
#[cfg(not(target_os = "windows"))]
{
macro_rules! handle_term_signal {
($sig: expr, $sender: expr) => {
tokio::spawn(async move {
$sig.recv().await;
$sender
.lock()
.await
.send(Message::Text(
serde_json::to_string(&Input::Terminate).unwrap(),
))
.await
.ok();
});
};
}
});
let mut sigint = tokio::signal::unix::signal(SignalKind::interrupt())?;
let sender_c = sender.clone();
handle_term_signal!(sigint, sender_c);
let mut sigterm = tokio::signal::unix::signal(SignalKind::terminate())?;
let sender_c = sender.clone();
handle_term_signal!(sigterm, sender_c);
let mut sighup = tokio::signal::unix::signal(SignalKind::hangup())?;
let sender_c = sender.clone();
handle_term_signal!(sighup, sender_c);
let mut sigwinch = tokio::signal::unix::signal(SignalKind::window_change())?;
let sender_c = sender.clone();
tokio::spawn(async move {
loop {
sigwinch.recv().await;
let Some(dimensions) = term_size::dimensions() else {
continue;
};
sender_c
.lock()
.await
.send(Message::Text(
serde_json::to_string(&Input::Resize(dimensions)).unwrap(),
))
.await
.ok();
}
});
}
macro_rules! handle_out {
($out: expr) => {
let Some(Ok(Message::Binary(b))) = receiver.next().await else {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论