mirror of
https://github.com/atuinsh/atuin.git
synced 2024-11-22 00:03:49 +01:00
wip: initial pty work
This commit is contained in:
parent
eb4a5ab4cd
commit
488bb2613c
184
Cargo.lock
generated
184
Cargo.lock
generated
@ -143,6 +143,12 @@ dependencies = [
|
||||
"password-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
||||
|
||||
[[package]]
|
||||
name = "async-stream"
|
||||
version = "0.3.5"
|
||||
@ -207,9 +213,11 @@ dependencies = [
|
||||
"atuin-daemon",
|
||||
"atuin-dotfiles",
|
||||
"atuin-history",
|
||||
"atuin-run",
|
||||
"atuin-server",
|
||||
"atuin-server-postgres",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"clap_complete_nushell",
|
||||
@ -383,6 +391,17 @@ dependencies = [
|
||||
"whoami",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atuin-run"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"eyre",
|
||||
"portable-pty",
|
||||
"tokio",
|
||||
"vt100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atuin-server"
|
||||
version = "18.3.0-prerelease.1"
|
||||
@ -1900,6 +1919,15 @@ dependencies = [
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ioctl-rs"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7970510895cee30b3e9128319f2cefd4bde883a39f38baa279567ba3a7eb97d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.9.0"
|
||||
@ -2232,6 +2260,20 @@ dependencies = [
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.25.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset",
|
||||
"pin-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
@ -2588,6 +2630,27 @@ version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
|
||||
|
||||
[[package]]
|
||||
name = "portable-pty"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "806ee80c2a03dbe1a9fb9534f8d19e4c0546b790cde8fd1fea9d6390644cb0be"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 1.3.2",
|
||||
"downcast-rs",
|
||||
"filedescriptor",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"nix 0.25.1",
|
||||
"serial",
|
||||
"shared_library",
|
||||
"shell-words",
|
||||
"winapi",
|
||||
"winreg 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
@ -2897,7 +2960,7 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"winreg",
|
||||
"winreg 0.50.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3280,6 +3343,48 @@ dependencies = [
|
||||
"syn 2.0.66",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1237a96570fc377c13baa1b88c7589ab66edced652e43ffb17088f003db3e86"
|
||||
dependencies = [
|
||||
"serial-core",
|
||||
"serial-unix",
|
||||
"serial-windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial-core"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f46209b345401737ae2125fe5b19a77acce90cd53e1658cda928e4fe9a64581"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial-unix"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f03fbca4c9d866e24a459cbca71283f545a37f8e3e002ad8c70593871453cab7"
|
||||
dependencies = [
|
||||
"ioctl-rs",
|
||||
"libc",
|
||||
"serial-core",
|
||||
"termios",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial-windows"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15c6d3b776267a75d31bbdfd5d36c0ca051251caafc285827052bc53bcdc8162"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"serial-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.6"
|
||||
@ -3311,6 +3416,22 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shared_library"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shell-words"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
|
||||
|
||||
[[package]]
|
||||
name = "shellexpand"
|
||||
version = "3.1.0"
|
||||
@ -3792,6 +3913,15 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termios"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5d9cf598a6d7ce700a4e6a9199da127e6819a61e64b68609683cc9a01b5683a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.61"
|
||||
@ -4315,6 +4445,39 @@ version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "vt100"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84cd863bf0db7e392ba3bd04994be3473491b31e66340672af5d11943c6274de"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"log",
|
||||
"unicode-width",
|
||||
"vte",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vte"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"utf8parse",
|
||||
"vte_generate_state_changes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vte_generate_state_changes"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
@ -4411,7 +4574,7 @@ dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"downcast-rs",
|
||||
"libc",
|
||||
"nix",
|
||||
"nix 0.24.3",
|
||||
"wayland-commons",
|
||||
"wayland-scanner",
|
||||
"wayland-sys",
|
||||
@ -4423,7 +4586,7 @@ version = "0.29.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"nix 0.24.3",
|
||||
"once_cell",
|
||||
"smallvec",
|
||||
"wayland-sys",
|
||||
@ -4677,6 +4840,15 @@ version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.50.0"
|
||||
@ -4696,7 +4868,7 @@ dependencies = [
|
||||
"derive-new",
|
||||
"libc",
|
||||
"log",
|
||||
"nix",
|
||||
"nix 0.24.3",
|
||||
"os_pipe",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
@ -4721,7 +4893,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "592b4883219f345e712b3209c62654ebda0bb50887f330cbd018d0f654bfd507"
|
||||
dependencies = [
|
||||
"gethostname",
|
||||
"nix",
|
||||
"nix 0.24.3",
|
||||
"winapi",
|
||||
"winapi-wsapoll",
|
||||
"x11rb-protocol",
|
||||
@ -4733,7 +4905,7 @@ version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56b245751c0ac9db0e006dc812031482784e434630205a93c73cfefcaabeac67"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"nix 0.24.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
19
crates/atuin-run/Cargo.toml
Normal file
19
crates/atuin-run/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "atuin-run"
|
||||
edition = "2021"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
readme.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
eyre.workspace = true
|
||||
tokio.workspace = true
|
||||
portable-pty = "0.8.1"
|
||||
vt100 = "0.15.2"
|
||||
bytes = "1.6.0"
|
1
crates/atuin-run/src/lib.rs
Normal file
1
crates/atuin-run/src/lib.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod run;
|
93
crates/atuin-run/src/run.rs
Normal file
93
crates/atuin-run/src/run.rs
Normal file
@ -0,0 +1,93 @@
|
||||
/// Create and manage pseudoterminals
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use eyre::{eyre, Result};
|
||||
|
||||
use bytes::Bytes;
|
||||
use portable_pty::{native_pty_system, CommandBuilder, MasterPty, PtySize};
|
||||
use tokio::sync::mpsc::{channel, Receiver, Sender};
|
||||
|
||||
pub struct Pty {
|
||||
tx: Sender<Bytes>,
|
||||
master: Box<dyn MasterPty>,
|
||||
}
|
||||
|
||||
impl Pty {
|
||||
pub async fn open_shell<'a>(rows: u16, cols: u16, shell: &str, dir: &str) -> Result<Self> {
|
||||
let sys = native_pty_system();
|
||||
|
||||
let pair = sys
|
||||
.openpty(PtySize {
|
||||
rows,
|
||||
cols,
|
||||
pixel_width: 0,
|
||||
pixel_height: 0,
|
||||
})
|
||||
.map_err(|e| eyre!("Failed to open pty: {}", e))?;
|
||||
|
||||
let cmd = CommandBuilder::new(shell);
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let mut child = pair.slave.spawn_command(cmd).unwrap();
|
||||
|
||||
// Wait for the child to exit
|
||||
let _ = child.wait().unwrap();
|
||||
|
||||
// Ensure slave is dropped
|
||||
// This closes file handles, we can deadlock if this is not done correctly.
|
||||
drop(pair.slave);
|
||||
});
|
||||
|
||||
// Handle input -> write to master writer
|
||||
let (master_tx, mut master_rx) = channel::<Bytes>(32);
|
||||
|
||||
let mut writer = pair.master.take_writer().unwrap();
|
||||
|
||||
tokio::spawn(async move {
|
||||
while let Some(bytes) = master_rx.recv().await {
|
||||
writer.write_all(&bytes).unwrap();
|
||||
writer.flush().unwrap();
|
||||
}
|
||||
|
||||
// When the channel has been closed, we won't be getting any more input. Close the
|
||||
// writer and the master.
|
||||
// This will also close the writer, which sends EOF to the underlying shell. Ensuring
|
||||
// that is also closed.
|
||||
drop(writer);
|
||||
});
|
||||
|
||||
Ok(Pty {
|
||||
tx: master_tx,
|
||||
master: pair.master,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn send_bytes(&self, bytes: Bytes) -> Result<()> {
|
||||
self.tx
|
||||
.send(bytes)
|
||||
.await
|
||||
.map_err(|e| eyre!("Failed to write to master tx: {}", e))
|
||||
}
|
||||
|
||||
pub async fn send_string(&self, cmd: &str) -> Result<()> {
|
||||
let bytes: Vec<u8> = cmd.bytes().collect();
|
||||
let bytes = Bytes::from(bytes);
|
||||
|
||||
self.send_bytes(bytes).await
|
||||
}
|
||||
|
||||
pub async fn send_single_string(&self, cmd: &str) -> Result<()> {
|
||||
let mut bytes: Vec<u8> = cmd.bytes().collect();
|
||||
bytes.push(0x04);
|
||||
|
||||
let bytes = Bytes::from(bytes);
|
||||
|
||||
self.send_bytes(bytes).await
|
||||
}
|
||||
|
||||
pub fn reader(&self) -> Result<Box<dyn Read + Send>> {
|
||||
self.master
|
||||
.try_clone_reader()
|
||||
.map_err(|e| eyre!("Failed to clone master reader: {}", e))
|
||||
}
|
||||
}
|
@ -49,6 +49,7 @@ atuin-common = { path = "../atuin-common", version = "18.3.0-prerelease.1" }
|
||||
atuin-dotfiles = { path = "../atuin-dotfiles", version = "0.2.0" }
|
||||
atuin-history = { path = "../atuin-history", version = "0.1.0" }
|
||||
atuin-daemon = { path = "../atuin-daemon", version = "0.1.0" }
|
||||
atuin-run = { path = "../atuin-run", version = "0.1.0" }
|
||||
|
||||
log = { workspace = true }
|
||||
env_logger = "0.11.2"
|
||||
@ -85,6 +86,7 @@ uuid = { workspace = true }
|
||||
unicode-segmentation = "1.11.0"
|
||||
sysinfo = "0.30.7"
|
||||
regex="1.10.4"
|
||||
bytes = "1.6.0"
|
||||
|
||||
[target.'cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))'.dependencies]
|
||||
cli-clipboard = { version = "0.4.0", optional = true }
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::path::PathBuf;
|
||||
use bytes::Bytes;
|
||||
use std::{io::BufRead, path::PathBuf};
|
||||
|
||||
use clap::Subcommand;
|
||||
use eyre::{Result, WrapErr};
|
||||
@ -83,6 +84,8 @@ pub enum Cmd {
|
||||
/// Print example configuration
|
||||
#[command()]
|
||||
DefaultConfig,
|
||||
|
||||
Boop,
|
||||
}
|
||||
|
||||
impl Cmd {
|
||||
@ -158,6 +161,28 @@ impl Cmd {
|
||||
#[cfg(feature = "daemon")]
|
||||
Self::Daemon => daemon::run(settings, sqlite_store, db).await,
|
||||
|
||||
Self::Boop => {
|
||||
let pty =
|
||||
atuin_run::run::Pty::open_shell(24, 18, "/bin/zsh", "/Users/ellie").await?;
|
||||
|
||||
pty.send_single_string("ls\necho 'foo'\nsleep 5\npwd\n")
|
||||
.await;
|
||||
|
||||
let mut reader = pty.reader().unwrap();
|
||||
tokio::task::spawn_blocking(|| {
|
||||
// Consume the output from the child
|
||||
// Can't read the full buffer, since that would wait for EOF
|
||||
let lines = std::io::BufReader::new(reader).lines();
|
||||
|
||||
for line in lines.flatten() {
|
||||
println!("{line}");
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user