Веб -терминал с использованием websocket в ржавчинеLinux

Ответить
Anonymous
 Веб -терминал с использованием websocket в ржавчине

Сообщение Anonymous »

Я хочу получить доступ к терминалу, используя веб -сокет в ржавчине, я реализовал этот код, но не могу использовать Top и Nano (интерактивные команды).
Пожалуйста, помогите мне сделать это.
Я пытался использовать ящик NIX.
Код уже дает правильный выход для обычных команд LS, PWD и т. Д.

Код: Выделить всё

use actix::{Actor, AsyncContext, StreamHandler};
use actix_web::{Error, HttpRequest};
use actix_web_actors::ws;
use nix::fcntl::{FcntlArg, OFlag};
use nix::libc::{O_NONBLOCK, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO, TIOCSCTTY};
use nix::pty::openpty;
use nix::sys::wait::{waitpid, WaitStatus};
use nix::unistd::{dup2, fork, setsid, ForkResult};
use std::io::{Read, Write};
use std::os::fd::IntoRawFd;

/// WebSocket upgrade handler
async fn terminal_session(
req: HttpRequest,
stream: web::Payload,
store: web::Data,
) -> Result {
let session_id = req.match_info().query("session_id").to_string();
ws::start(
TerminalSession {
session_id,
store: store.get_ref().clone(),
},
&req,
stream,
)
}

/// WebSocket connection handler
struct TerminalSession {
session_id: String,
store: Arc,
}

impl Actor for TerminalSession {
type Context = ws::WebsocketContext;
}

impl StreamHandler for TerminalSession {
fn handle(&mut self, msg: Result, ctx: &mut Self::Context) {
if let Ok(ws::Message::Text(command)) = msg {
let mut sessions = self.store.lock().unwrap();

if let Some(current_dir) = sessions.get_mut(&self.session_id) {
let pty = openpty(None, None).unwrap();
let master_fd = pty.master.into_raw_fd(); // Prevent Rust from closing it early
let slave_fd = pty.slave.into_raw_fd();

println!(
"PTY Initialized -> Master FD: {}, Slave FD: {}",
master_fd, slave_fd
);

match unsafe { fork() } {
Ok(ForkResult::Parent { child }) => {
// Parent Process: Set Non-Blocking Mode4
use nix::fcntl::{fcntl, FcntlArg, OFlag};

if let Err(e) = fcntl(master_fd, FcntlArg::F_SETFL(OFlag::O_NONBLOCK)) {
eprintln!("Failed to set non-blocking PTY: {}", e);
}

// Stream PTY Output Continuously
let ctx_addr = ctx.address();
actix_rt::spawn(async move {
let mut master_reader =
unsafe { std::fs::File::from_raw_fd(master_fd) };
let mut buffer = [0u8; 4096];

loop {
match master_reader.read(&mut buffer) {
Ok(0) => {
println!("PTY Stream EOF reached.");
break;
}
Ok(n) => {
let output =
String::from_utf8_lossy(&buffer[..n]).to_string();
println!("PTY Output -> {}", output);
ctx_addr.do_send(WsOutput(output));
}
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
// No data available, yield
actix_rt::task::yield_now().await;
}
Err(e) => {
eprintln!("PTY Read Error: {}", e);
break;
}
}
}
});

// Handle Child Process Cleanup
actix_rt::spawn(async move {
match waitpid(child, None) {
Ok(WaitStatus::Exited(_, status)) => {
println!("Child process exited with status: {}", status);
}
Ok(WaitStatus::Signaled(_, signal, _)) =>  {
println!("Child process terminated by signal: {:?}", signal);
}
Ok(_) => println!("Child process exited."),
Err(e) => eprintln!("waitpid error: {}", e),
}
});
}
Ok(ForkResult::Child) => {
// Child Process: Configure PTY and Execute Command
unsafe {
setsid().expect("Failed to create new session");
libc::ioctl(slave_fd, TIOCSCTTY, 0); // Set PTY as controlling terminal
dup2(slave_fd, STDIN_FILENO).expect("dup2 stdin failed");
dup2(slave_fd, STDOUT_FILENO).expect("dup2 stdout failed");
dup2(slave_fd, STDERR_FILENO).expect("dup2 stderr failed");
// Set PTY to Raw Mode for `nano`, `top`, etc.
use nix::sys::termios::{tcsetattr, LocalFlags, SetArg, Termios};
let termios =
Termios::from_fd(slave_fd).expect("Failed to get termios");
let mut new_termios = termios;
new_termios
.local_flags
.remove(LocalFlags::ICANON | LocalFlags::ECHO);
tcsetattr(slave_fd, SetArg::TCSANOW, &new_termios)
.expect("Failed to set raw mode");

libc::close(slave_fd);
}

std::env::set_current_dir(current_dir).unwrap_or_else(|e| {
eprintln!("Failed to set current directory: {}", e);
});
std::env::set_var("TERM", "xterm-256color");

let shell = CString::new("bash").unwrap();
let args = CString::new("-c").unwrap();
let command = CString::new(command.to_string()).unwrap();

nix::unistd::execvp(
&shell,
&[shell.as_c_str(), args.as_c_str(), command.as_c_str()],
)
.expect("Failed to execute command");
}
Err(e) => {
ctx.text(format!("Fork failed: {}", e));
}
}
} else {
ctx.text("Session not found. Please reconnect.");
ctx.close(Some(ws::CloseReason {
code: ws::CloseCode::Normal,
description: Some("Session not found".into()),
}));
}
}
}
}

/// WebSocket message handler for streaming PTY output
struct WsOutput(String);

impl actix::Message for WsOutput {
type Result = ();
}

impl actix::Handler for TerminalSession {
type Result = ();

fn handle(&mut self, msg: WsOutput, ctx: &mut Self::Context) {
ctx.text(msg.0); // Send output to WebSocket client
}
}
Я попытался ко всем возможным способам, но все еще оставался, чтобы найти решение.

Подробнее здесь: https://stackoverflow.com/questions/794 ... et-in-rust
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Linux»