diff --git a/installer/src/main.rs b/installer/src/main.rs index 1a40633..285c2ff 100644 --- a/installer/src/main.rs +++ b/installer/src/main.rs @@ -1,4 +1,4 @@ -use anyhow::{Context, Error}; +use anyhow::{Context, Error, bail}; use clap::{Parser, Subcommand}; mod orbic; @@ -20,6 +20,8 @@ enum Command { Orbic(InstallOrbic), /// Install rayhunter on the TP-Link M7350. Tplink(InstallTpLink), + /// Developer utilities. + Util(Util), } #[derive(Parser, Debug)] @@ -37,12 +39,51 @@ struct InstallTpLink { #[derive(Parser, Debug)] struct InstallOrbic {} +#[derive(Parser, Debug)] +struct Util { + #[command(subcommand)] + command: UntilSubCommand, +} + +#[derive(Subcommand, Debug)] +enum UntilSubCommand { + /// Send a serial command to the Orbic. + Serial(Serial), +} + +#[derive(Parser, Debug)] +struct Serial { + #[arg(long)] + root: bool, + command: Vec, +} + async fn run_function() -> Result<(), Error> { let Args { command } = Args::parse(); match command { Command::Tplink(tplink) => tplink::main_tplink(tplink).await.context("Failed to install rayhunter on the TP-Link M7350. Make sure your computer is connected to the hotspot using USB tethering or WiFi. Currently only Hardware Revision v3 is supported.")?, Command::Orbic(_) => orbic::install().await.context("Failed to install rayhunter on the Orbic RC400L")?, + Command::Util(subcommand) => match subcommand.command { + UntilSubCommand::Serial(serial_cmd) => { + if serial_cmd.root { + if !serial_cmd.command.is_empty() { + eprintln!("You cannot use --root and specify a command at the same time"); + std::process::exit(64); + } + orbic::enable_command_mode()?; + } else if serial_cmd.command.is_empty() { + eprintln!("Command cannot be an empty string"); + std::process::exit(64); + } else { + let cmd = serial_cmd.command.join(" "); + match orbic::open_orbic()? { + Some(interface) => orbic::send_serial_cmd(&interface, &cmd).await?, + None => bail!(orbic::ORBIC_NOT_FOUND), + } + } + } + } } Ok(()) diff --git a/installer/src/orbic.rs b/installer/src/orbic.rs index b2c9103..3511ed2 100644 --- a/installer/src/orbic.rs +++ b/installer/src/orbic.rs @@ -13,7 +13,7 @@ use tokio_stream::StreamExt; use crate::{CONFIG_TOML, RAYHUNTER_DAEMON_INIT}; -const ORBIC_NOT_FOUND: &str = r#"No Orbic device found. +pub const ORBIC_NOT_FOUND: &str = r#"No Orbic device found. Make sure your device is plugged in and turned on. If you're sure you've plugged in an Orbic device via USB, there may be a bug in @@ -303,13 +303,16 @@ async fn wait_for_usb_device(vendor_id: u16, product_id: u16) -> Result<()> { } } +async fn at_syscmd(interface: &Interface, command: &str) -> Result<()> { + send_serial_cmd(interface, &format!("AT+SYSCMD={command}")).await +} /// Sends an AT command to the usb device over the serial port /// /// First establish a USB handle and context by calling `open_orbic() -async fn at_syscmd(interface: &Interface, command: &str) -> Result<()> { +pub async fn send_serial_cmd(interface: &Interface, command: &str) -> Result<()> { let mut data = String::new(); data.push_str("\r\n"); - data.push_str(&format!("AT+SYSCMD={command}")); + data.push_str(command); data.push_str("\r\n"); let timeout = Duration::from_secs(2); @@ -362,7 +365,7 @@ async fn at_syscmd(interface: &Interface, command: &str) -> Result<()> { /// Send a command to switch the device into generic mode, exposing serial /// /// If the device reboots while the command is still executing you may get a pipe error here, not sure what to do about this race condition. -fn enable_command_mode() -> Result<()> { +pub fn enable_command_mode() -> Result<()> { if open_orbic()?.is_some() { println!("Device already in command mode. Doing nothing..."); return Ok(()); @@ -396,7 +399,7 @@ fn enable_command_mode() -> Result<()> { } /// Get an Interface for the orbic device -fn open_orbic() -> Result> { +pub fn open_orbic() -> Result> { // Device after initial mode switch if let Some(device) = open_usb_device(VENDOR_ID, PRODUCT_ID)? { let interface = device