Finally works, now the commands it's launched
parent
e76881005c
commit
31d13507fe
|
|
@ -1231,6 +1231,7 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
|
|||
name = "sshmngrs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"ratatui",
|
||||
"serde",
|
||||
"toml",
|
||||
|
|
|
|||
|
|
@ -9,3 +9,6 @@ ratatui = "0.30.2"
|
|||
serde = { version = "1.0.229", features = ["derive"] }
|
||||
toml = "1.1.3"
|
||||
tui-input = "0.15.3"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2.189"
|
||||
|
|
|
|||
74
src/main.rs
74
src/main.rs
|
|
@ -1,7 +1,13 @@
|
|||
mod misc;
|
||||
|
||||
use misc::{
|
||||
centered_rect, ignore_interrupt_signals, reset_child_interrupt_signals,
|
||||
restore_interrupt_signals, wait_for_enter,
|
||||
};
|
||||
use ratatui::{
|
||||
DefaultTerminal, Frame,
|
||||
crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers},
|
||||
layout::{Constraint, Flex, Layout, Rect},
|
||||
layout::{Constraint, Layout, Rect},
|
||||
style::{Color, Style},
|
||||
widgets::{Block, Borders, Clear, List, ListItem, ListState, Paragraph},
|
||||
};
|
||||
|
|
@ -126,6 +132,7 @@ struct App {
|
|||
config: Config,
|
||||
selected: usize,
|
||||
mode: Mode,
|
||||
pending_command: Option<String>,
|
||||
should_quit: bool,
|
||||
}
|
||||
|
||||
|
|
@ -137,6 +144,7 @@ impl App {
|
|||
config,
|
||||
selected: 0,
|
||||
mode: Mode::Normal,
|
||||
pending_command: None,
|
||||
should_quit: false,
|
||||
}
|
||||
}
|
||||
|
|
@ -170,6 +178,7 @@ impl App {
|
|||
KeyCode::Char('a') => self.mode = Mode::Editing(CommandForm::new()),
|
||||
KeyCode::Char('e') => self.start_editing(),
|
||||
KeyCode::Char('d') => self.delete_selected(),
|
||||
KeyCode::Enter => self.connect_selected(),
|
||||
KeyCode::Up | KeyCode::Char('k') => self.select_previous(),
|
||||
KeyCode::Down | KeyCode::Char('j') => self.select_next(),
|
||||
_ => {}
|
||||
|
|
@ -192,6 +201,12 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
fn connect_selected(&mut self) {
|
||||
if let Some(command) = self.config.commands.get(self.selected) {
|
||||
self.pending_command = Some(command.command.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn on_key_editing(&mut self, key: KeyEvent) {
|
||||
match key.code {
|
||||
KeyCode::Esc => self.mode = Mode::Normal,
|
||||
|
|
@ -253,10 +268,59 @@ fn run(mut terminal: DefaultTerminal) -> std::io::Result<()> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(command) = app.pending_command.take() {
|
||||
launch_command(&mut terminal, &command)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn launch_command(terminal: &mut DefaultTerminal, command: &str) -> std::io::Result<()> {
|
||||
ratatui::restore();
|
||||
|
||||
println!("Running: {command}\n");
|
||||
|
||||
// In Unix, while the child runs the command is in cooked mode, so Ctrl+C sends
|
||||
// SIGINT to the whole foreground process group (sshmngrs included). Ignore it in
|
||||
// the parent so interrupting the child, for example, at an ssh password prompt,
|
||||
// returns to sshmngrs instead of killing it. The child resets its own handlers to
|
||||
// default below so it can still be interrupted.
|
||||
ignore_interrupt_signals();
|
||||
let mut shell = shell_command(command);
|
||||
reset_child_interrupt_signals(&mut shell);
|
||||
|
||||
let status = shell.status();
|
||||
|
||||
match &status {
|
||||
Ok(code) if !code.success() => eprintln!("\nCommand exited with {code}."),
|
||||
Ok(code) => println!("\nCommand exited with {code}."),
|
||||
Err(error) => eprintln!("\nCould not run command: {error}"),
|
||||
}
|
||||
|
||||
wait_for_enter("Press Enter to restore sshmngrs...");
|
||||
|
||||
restore_interrupt_signals();
|
||||
|
||||
*terminal = ratatui::init();
|
||||
terminal.clear()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn shell_command(command: &str) -> std::process::Command {
|
||||
let mut shell = if cfg!(target_os = "windows") {
|
||||
let mut shell = std::process::Command::new("cmd");
|
||||
shell.arg("/C");
|
||||
shell
|
||||
} else {
|
||||
let mut shell = std::process::Command::new("sh");
|
||||
shell.arg("-c");
|
||||
shell
|
||||
};
|
||||
shell.arg(command);
|
||||
shell
|
||||
}
|
||||
|
||||
fn draw(frame: &mut Frame, app: &App) {
|
||||
draw_main(frame, app, frame.area());
|
||||
|
||||
|
|
@ -342,11 +406,3 @@ fn draw_field(frame: &mut Frame, area: Rect, title: &str, input: &Input, focused
|
|||
frame.set_cursor_position((cursor_x, cursor_y));
|
||||
}
|
||||
}
|
||||
|
||||
fn centered_rect(area: Rect, width: u16, height: u16) -> Rect {
|
||||
let horizontal = Layout::horizontal([Constraint::Length(width)]).flex(Flex::Center);
|
||||
let vertical = Layout::vertical([Constraint::Length(height)]).flex(Flex::Center);
|
||||
let [area] = vertical.areas(area);
|
||||
let [area] = horizontal.areas(area);
|
||||
area
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
||||
|
||||
pub fn wait_for_enter(prompt: &str) {
|
||||
println!("{prompt}");
|
||||
let mut discard = String::new();
|
||||
let _ = std::io::stdin().read_line(&mut discard);
|
||||
}
|
||||
|
||||
pub fn centered_rect(area: Rect, width: u16, height: u16) -> Rect {
|
||||
let horizontal = Layout::horizontal([Constraint::Length(width)]).flex(Flex::Center);
|
||||
let vertical = Layout::vertical([Constraint::Length(height)]).flex(Flex::Center);
|
||||
let [area] = vertical.areas(area);
|
||||
let [area] = horizontal.areas(area);
|
||||
area
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn ignore_interrupt_signals() {
|
||||
unsafe {
|
||||
libc::signal(libc::SIGINT, libc::SIG_IGN);
|
||||
libc::signal(libc::SIGQUIT, libc::SIG_IGN);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn restore_interrupt_signals() {
|
||||
unsafe {
|
||||
libc::signal(libc::SIGINT, libc::SIG_DFL);
|
||||
libc::signal(libc::SIGQUIT, libc::SIG_DFL);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn reset_child_interrupt_signals(command: &mut std::process::Command) {
|
||||
use std::os::unix::process::CommandExt;
|
||||
// Safety: the closure only resets signal dispositions to their default,
|
||||
// which is async-signal-safe and valid between fork and exec.
|
||||
unsafe {
|
||||
command.pre_exec(|| {
|
||||
libc::signal(libc::SIGINT, libc::SIG_DFL);
|
||||
libc::signal(libc::SIGQUIT, libc::SIG_DFL);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
pub fn ignore_interrupt_signals() {}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
pub fn restore_interrupt_signals() {}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
pub fn reset_child_interrupt_signals(_command: &mut std::process::Command) {}
|
||||
Loading…
Reference in New Issue