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

advanced terminal commands

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