diff --git a/Cargo.lock b/Cargo.lock index d5dd291..369c921 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -137,6 +137,12 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "camino" version = "1.1.6" @@ -165,6 +171,7 @@ dependencies = [ "colored", "either", "enum_dispatch", + "local-ip-address", "rustc_version", "serde", "serde_json", @@ -246,7 +253,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -293,7 +300,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -410,7 +417,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn", + "syn 2.0.38", ] [[package]] @@ -431,6 +438,18 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45786cec4d5e54a224b15cb9f06751883103a27c19c93eda09b0b4f5f08fefac" +[[package]] +name = "local-ip-address" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66357e687a569abca487dc399a9c9ac19beb3f13991ed49f00c144e02cbd42ab" +dependencies = [ + "libc", + "neli", + "thiserror", + "windows-sys", +] + [[package]] name = "log" version = "0.4.20" @@ -452,6 +471,31 @@ dependencies = [ "adler", ] +[[package]] +name = "neli" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1100229e06604150b3becd61a4965d5c70f3be1759544ea7274166f4be41ef43" +dependencies = [ + "byteorder", + "libc", + "log", + "neli-proc-macros", +] + +[[package]] +name = "neli-proc-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c168194d373b1e134786274020dae7fc5513d565ea2ebb9bc9ff17ffb69106d4" +dependencies = [ + "either", + "proc-macro2", + "quote", + "serde", + "syn 1.0.109", +] + [[package]] name = "num-traits" version = "0.2.17" @@ -601,7 +645,7 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -633,6 +677,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.38" @@ -680,7 +735,7 @@ checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -726,7 +781,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.38", "wasm-bindgen-shared", ] @@ -748,7 +803,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/Cargo.toml b/Cargo.toml index 0eea27e..1902355 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,14 +25,4 @@ suppaftp = { version = "5.2.1", features = ["no-log"] } tee = "0.1.0" tempfile = "3.8.0" walkdir = "2.4.0" - -[package.metadata.vita] -title_id = "VITASHELL" -title_name = "Test app" -assets = "static" -# You can choose a subset of std or use panic_abort if you don't need unwinding -build_std = "std,panic_unwind" -# You can provide a custom JSON file spec -vita_strip_flags = ["-g"] -vita_make_fself_flags = ["-s"] -vita_mksfoex_flags = ["-d", "ATTRIBUTE2=12"] +local-ip-address = "0.5.6" diff --git a/src/commands/logs.rs b/src/commands/logs.rs index 92163fe..de5421b 100644 --- a/src/commands/logs.rs +++ b/src/commands/logs.rs @@ -1,19 +1,120 @@ -use std::{io::Read, net::TcpListener}; +use std::{ + io::{Cursor, Read}, + net::{Ipv4Addr, TcpListener}, + str::FromStr, +}; -use anyhow::Context; -use clap::Args; +use anyhow::{bail, Context}; +use clap::{Args, Subcommand}; use colored::Colorize; -use super::Executor; +use crate::ftp; + +use super::{ConnectionArgs, Executor}; #[derive(Args, Debug)] pub struct Logs { + #[command(subcommand)] + cmd: Option, + #[arg(long, short = 'p', env = "VITA_LOG_PORT", default_value_t = 8888)] port: u16, } -impl Executor for Logs { - fn execute(&self, verbose: u8) -> anyhow::Result<()> { +#[derive(Subcommand, Debug)] +pub enum LogsCmd { + /// Start a TCP server on 0.0.0.0 and print to stdout all bytes read from the socket + Listen, + /// Reconfigures PrincessLog via vita-companion. + /// This will upload the configuration file with the ip address of your host and a port to your Vita. + Configure(Configure), +} + +#[derive(Args, Debug)] +pub struct Configure { + #[command(flatten)] + pub connection: ConnectionArgs, + + #[arg(long)] + host_ip_address: Option, + + #[arg(long, default_value = "false")] + kernel_debug: bool, +} + +static MAGIC: &[u8] = b"NLM\0"; +static NLM_CONFIG_FLAGS_BIT_QAF_DEBUG_PRINTF: u32 = 1 << 0; + +impl Logs { + fn configure(&self, configure: &Configure, verbose: u8) -> anyhow::Result<()> { + let filename = "ur0:/data/NetLoggingMgrConfig.bin"; + println!( + "{} {filename}", + "Downloading the existing config from".blue() + ); + let mut ftp = ftp::connect(&configure.connection, verbose)?; + + let file = ftp.retr_as_buffer(filename); + if let Ok(mut config) = file { + println!("{}", "Found existing config".blue()); + let mut buffer = [0; 4]; + config.read_exact(&mut buffer)?; + if buffer != MAGIC { + println!("{}", "Existing config is invalid".red()); + } + + config.read_exact(&mut buffer)?; + let ip = Ipv4Addr::from(buffer); + config.read_exact(&mut buffer)?; + let flags = u32::from_le_bytes(buffer); + + let mut buffer = [0; 2]; + config.read_exact(&mut buffer)?; + let port = u16::from_le_bytes(buffer); + + println!("{}: {ip}:{port}", "Current log address".yellow()); + let kdbg = (flags & NLM_CONFIG_FLAGS_BIT_QAF_DEBUG_PRINTF) != 0; + println!("{}: {kdbg}", "Kernel debug print".yellow()); + } + + let ip = match &configure.host_ip_address { + Some(ip) => Ipv4Addr::from_str(ip)?, + None => match local_ip_address::local_ip()? { + std::net::IpAddr::V4(ip) => ip, + std::net::IpAddr::V6(_) => bail!("Unable to guess host ip address"), + }, + }; + + println!( + "{} {ip}:{port} {} {kdbg}", + "Setting log address to".blue(), + "and kernel debug print to".blue(), + port = self.port, + kdbg = configure.kernel_debug, + ); + + let mut config = Vec::new(); + config.extend_from_slice(MAGIC); + config.extend_from_slice(&ip.octets()); + + let flags = match configure.kernel_debug { + true => NLM_CONFIG_FLAGS_BIT_QAF_DEBUG_PRINTF, + false => 0, + }; + + config.extend_from_slice(&flags.to_le_bytes()); + config.extend_from_slice(&self.port.to_le_bytes()); + config.extend_from_slice(&[0, 0]); + + print!("{} {}", "Saving config to".blue(), filename); + let _ = ftp.mkdir("ur0:/data/"); + ftp.put_file(filename, &mut Cursor::new(config))?; + + println!("Config will take effect after Vita is rebooted"); + + Ok(()) + } + fn listen(&self, verbose: u8) -> anyhow::Result<()> { if verbose > 0 { println!("{} {}", "Starting TCP server on port".blue(), self.port); } @@ -62,3 +163,12 @@ impl Executor for Logs { Ok(()) } } + +impl Executor for Logs { + fn execute(&self, verbose: u8) -> anyhow::Result<()> { + match self.cmd.as_ref() { + Some(LogsCmd::Configure(cmd)) => self.configure(cmd, verbose), + Some(LogsCmd::Listen) | None => self.listen(verbose), + } + } +}