Skip to content

Commit

Permalink
feat: should make crate usable on Windows (#35)
Browse files Browse the repository at this point in the history
Co-authored-by: LIAUD Corentin <[email protected]>
  • Loading branch information
cocool97 and LIAUD Corentin authored Oct 1, 2024
1 parent 2f39c13 commit ed884b0
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 66 deletions.
5 changes: 4 additions & 1 deletion adb_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ adb_client = { version = "1.0.6" }
anyhow = { version = "1.0.89" }
clap = { version = "4.5.18", features = ["derive"] }
env_logger = { version = "0.11.5" }
log = "0.4.22"
log = { version = "0.4.22" }

[target.'cfg(unix)'.dependencies]
termios = { version = "0.3.3" }
2 changes: 2 additions & 0 deletions adb_client/src/adb_termios.rs → adb_cli/src/adb_termios.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(any(target_os = "linux", target_os = "macos"))]

use std::os::unix::prelude::{AsRawFd, RawFd};

use termios::{tcsetattr, Termios, TCSANOW, VMIN, VTIME};
Expand Down
16 changes: 15 additions & 1 deletion adb_cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#[cfg(any(target_os = "linux", target_os = "macos"))]
mod adb_termios;
mod commands;
mod models;

Expand Down Expand Up @@ -53,7 +55,19 @@ fn main() -> Result<()> {
}
LocalCommand::Shell { command } => {
if command.is_empty() {
device.shell()?;
// Need to duplicate some code here as ADBTermios [Drop] implementation resets terminal state.
// Using a scope here would call drop() too early..
#[cfg(any(target_os = "linux", target_os = "macos"))]
{
let mut adb_termios = adb_termios::ADBTermios::new(std::io::stdin())?;
adb_termios.set_adb_termios()?;
device.shell(std::io::stdin(), std::io::stdout())?;
}

#[cfg(not(any(target_os = "linux", target_os = "macos")))]
{
device.shell(std::io::stdin(), std::io::stdout())?;
}
} else {
device.shell_command(command, std::io::stdout())?;
}
Expand Down
6 changes: 2 additions & 4 deletions adb_client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@ version.workspace = true
[dependencies]
byteorder = { version = "1.5.0" }
chrono = { version = "0.4.38" }
homedir = "0.3.3"
homedir = { version = "0.3.3" }
image = { version = "0.25.2" }
lazy_static = { version = "1.5.0" }
log = "0.4.22"
mio = { version = "1.0.2", features = ["os-ext", "os-poll"] }
log = { version = "0.4.22" }
regex = { version = "1.10.6", features = ["perf", "std", "unicode"] }
termios = { version = "0.3.3" }
thiserror = { version = "1.0.64" }
1 change: 0 additions & 1 deletion adb_client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#![forbid(missing_docs)]
#![doc = include_str!("../README.md")]

mod adb_termios;
mod constants;
mod emulator;
mod error;
Expand Down
78 changes: 19 additions & 59 deletions adb_client/src/server/device_commands/shell.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,11 @@
use std::{
io::{self, Read, Write},
sync::mpsc,
time::Duration,
};

use mio::{unix::SourceFd, Events, Interest, Poll, Token};
use std::io::{ErrorKind, Read, Write};

use crate::{
adb_termios::ADBTermios,
models::{AdbServerCommand, HostFeatures},
ADBServerDevice, Result, RustADBError,
};

const STDIN: Token = Token(0);
const BUFFER_SIZE: usize = 512;
const POLL_DURATION: Duration = Duration::from_millis(100);

fn setup_poll_stdin() -> std::result::Result<Poll, io::Error> {
let poll = Poll::new()?;
let stdin_fd = 0;
poll.registry()
.register(&mut SourceFd(&stdin_fd), STDIN, Interest::READABLE)?;

Ok(poll)
}

impl ADBServerDevice {
/// Runs 'command' in a shell on the device, and write its output and error streams into [`output`].
Expand Down Expand Up @@ -73,13 +55,14 @@ impl ADBServerDevice {
}
}

/// Starts an interactive shell session on the device. Redirects stdin/stdout/stderr as appropriate.
pub fn shell(&mut self) -> Result<()> {
let mut adb_termios = ADBTermios::new(std::io::stdin())?;
adb_termios.set_adb_termios()?;

// TODO: FORWARD CTRL+C !!

/// Starts an interactive shell session on the device.
/// Input data is read from [reader] and write to [writer].
/// [W] has a 'static bound as it is internally used in a thread.
pub fn shell<R: Read, W: Write + Send + 'static>(
&mut self,
mut reader: R,
mut writer: W,
) -> Result<()> {
let supported_features = self.host_features()?;
if !supported_features.contains(&HostFeatures::ShellV2)
&& !supported_features.contains(&HostFeatures::Cmd)
Expand All @@ -93,26 +76,22 @@ impl ADBServerDevice {
self.get_transport_mut()
.send_adb_request(AdbServerCommand::Shell)?;

// let read_stream = Arc::new(self.tcp_stream);
let mut read_stream = self.get_transport_mut().get_raw_connection()?.try_clone()?;

let (tx, rx) = mpsc::channel::<bool>();

let mut write_stream = read_stream.try_clone()?;

// Reading thread
// Reading thread, reads response from adb-server
std::thread::spawn(move || -> Result<()> {
loop {
let mut buffer = [0; BUFFER_SIZE];
match read_stream.read(&mut buffer) {
Ok(0) => {
let _ = tx.send(true);
read_stream.shutdown(std::net::Shutdown::Both)?;
return Ok(());
}
Ok(size) => {
std::io::stdout().write_all(&buffer[..size])?;
std::io::stdout().flush()?;
writer.write_all(&buffer[..size])?;
writer.flush()?;
}
Err(e) => {
return Err(RustADBError::IOError(e));
Expand All @@ -121,33 +100,14 @@ impl ADBServerDevice {
}
});

let mut buf = [0; BUFFER_SIZE];
let mut events = Events::with_capacity(1);

let mut poll = setup_poll_stdin()?;

// Polling either by checking that reading socket hasn't been closed, and if is there is something to read on stdin.
loop {
poll.poll(&mut events, Some(POLL_DURATION))?;
match rx.try_recv() {
Ok(_) | Err(mpsc::TryRecvError::Disconnected) => return Ok(()),
Err(mpsc::TryRecvError::Empty) => (),
}

for event in events.iter() {
match event.token() {
STDIN => {
let size = match std::io::stdin().read(&mut buf) {
Ok(0) => return Ok(()),
Ok(size) => size,
Err(_) => return Ok(()),
};

write_stream.write_all(&buf[0..size])?;
}
_ => unreachable!(),
}
// Read from given reader (that could be stdin e.g), and write content to server socket
if let Err(e) = std::io::copy(&mut reader, &mut write_stream) {
match e.kind() {
ErrorKind::BrokenPipe => return Ok(()),
_ => return Err(RustADBError::IOError(e)),
}
}

Ok(())
}
}

0 comments on commit ed884b0

Please sign in to comment.