diff --git a/Cargo.lock b/Cargo.lock index daea3f3..938bee6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,14 +5,3 @@ version = 4 [[package]] name = "wolz" version = "0.0.1" -dependencies = [ - "wolz-lib", -] - -[[package]] -name = "wolz-lib" -version = "0.0.1" - -[[package]] -name = "wolzd" -version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index a7cff58..98e3b21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,4 @@ -[workspace] -members = [ - "crates/*", -] -resolver = "2" +[package] +name = "wolz" +version = "0.0.1" +edition = "2024" diff --git a/crates/wolz-client/Cargo.lock b/crates/wolz-client/Cargo.lock deleted file mode 100644 index 938bee6..0000000 --- a/crates/wolz-client/Cargo.lock +++ /dev/null @@ -1,7 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "wolz" -version = "0.0.1" diff --git a/crates/wolz-client/Cargo.toml b/crates/wolz-client/Cargo.toml deleted file mode 100644 index a113b73..0000000 --- a/crates/wolz-client/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "wolz" -version = "0.0.1" -edition = "2024" - -[dependencies] -wolz-lib = { path = "../wolz-lib" } diff --git a/crates/wolz-client/src/main.rs b/crates/wolz-client/src/main.rs deleted file mode 100644 index 7dc6ee7..0000000 --- a/crates/wolz-client/src/main.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::env::args; - -use wolz_lib::{error::WResult, wolz::Wolz}; - -fn main() -> WResult<()> { - let arg_1 = args().nth(1); - let Some(arg_1) = arg_1 else { - print_usage(); - return Ok(()); - }; - - match arg_1.as_str() { - "-h" | "--help" => { - print_usage(); - return Ok(()); - } - _ => { - let wolz = Wolz::create()?; - wolz.wake_up(&arg_1)?; - Ok(()) - } - } -} - -fn print_usage() { - println!( - "{}", - r#"usage: woly ARG - -Options: - ARG Either the mac address of the device to send the wol magic - packet to, or a nickname specified in woly.conf to lookup - the mac. See details about config file below. - Eg: woly aa:bb:cc:dd:ee:ff - Eg: woly potato - -Config file: - Filename woly.conf - Location $WOLY_CONFIG_DIR/woly.conf, then $XDG_CONFIG_DIR/woly.conf, - then $HOME/woly.conf - Format KEY=VALUE - one per line without spaces, no comments allowed - Example potato=aa:bb:cc:dd:ee:ff - idly=12:34:23:e2:3d:ff -"# - ); -} diff --git a/crates/wolz-daemon/Cargo.toml b/crates/wolz-daemon/Cargo.toml deleted file mode 100644 index 8fbb7e0..0000000 --- a/crates/wolz-daemon/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[package] -name = "wolzd" -version = "0.0.1" -edition = "2024" diff --git a/crates/wolz-daemon/src/error.rs b/crates/wolz-daemon/src/error.rs deleted file mode 100644 index 1510eb9..0000000 --- a/crates/wolz-daemon/src/error.rs +++ /dev/null @@ -1,28 +0,0 @@ -use std::{fmt::Display, process::Output}; - -pub type DResult = Result; - -#[derive(Debug)] -pub(crate) enum DError { - InvalidRequest(String), - TcpBindError(std::io::Error), - ConnectionReadError(std::io::Error), - CommandError(String), - CommandExecutionError(Output), -} - -impl Display for DError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - DError::CommandError(s) => write!(f, "Failed to execute command {}", s), - DError::CommandExecutionError(output) => { - writeln!(f, "Exit Code : {}", output.status.code().unwrap())?; - writeln!(f, "Stdout : {}", String::from_utf8_lossy(&output.stdout))?; - writeln!(f, "Stderr : {}", String::from_utf8_lossy(&output.stderr)) - }, - DError::TcpBindError(e) => write!(f, "Failed to create tcp listener {}", e), - DError::ConnectionReadError(e) => write!(f, "Error while reading from the socket {}", e), - DError::InvalidRequest(s) => write!(f, "Command '{}' in request is invalid, dropping it", s), - } - } -} diff --git a/crates/wolz-lib/Cargo.toml b/crates/wolz-lib/Cargo.toml deleted file mode 100644 index f5d6de8..0000000 --- a/crates/wolz-lib/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[package] -name = "wolz-lib" -version = "0.0.1" -edition = "2024" diff --git a/crates/wolz-lib/src/error.rs b/crates/wolz-lib/src/error.rs deleted file mode 100644 index 90c4723..0000000 --- a/crates/wolz-lib/src/error.rs +++ /dev/null @@ -1,32 +0,0 @@ -use std::fmt::Display; - -pub type WResult = std::result::Result; - -#[derive(Debug)] -pub enum WError { - ParseMacError(String), - NotAMac(String), - NickNotAlphanumeric(String), - NickNotMapped(String), - InvalidConfigDir(String), - ConfigDirInitError(String), - ConfigFileFormatError(String), - UdpSocketBindError(String), -} - -impl Display for WError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let to_write = match self { - WError::ParseMacError(s) => format!("{}", s), - WError::NotAMac(s) => format!("'{}' is not a valid MAC", s), - WError::NickNotAlphanumeric(s) => format!("{}", s), - WError::NickNotMapped(s) => format!("{}", s), - WError::InvalidConfigDir(s) => format!("{}", s), - WError::ConfigDirInitError(s) => format!("{}", s), - WError::ConfigFileFormatError(s) => format!("{}", s), - WError::UdpSocketBindError(s) => format!("{}", s), - }; - - write!(f, "{}", to_write) - } -} diff --git a/crates/wolz-lib/src/lib.rs b/crates/wolz-lib/src/lib.rs deleted file mode 100644 index 096c2ed..0000000 --- a/crates/wolz-lib/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod error; -pub(crate) mod mac; -pub(crate) mod config; -mod ifaddrs; -pub mod wolz; diff --git a/crates/wolz-lib/src/config.rs b/src/config.rs similarity index 100% rename from crates/wolz-lib/src/config.rs rename to src/config.rs diff --git a/crates/wolz-daemon/src/main.rs b/src/daemon.rs similarity index 60% rename from crates/wolz-daemon/src/main.rs rename to src/daemon.rs index 1bbd924..b91fa38 100644 --- a/crates/wolz-daemon/src/main.rs +++ b/src/daemon.rs @@ -1,6 +1,4 @@ -mod error; - -use crate::error::{DError, DResult}; +use crate::error::{WError, WResult}; use std::{ io::Read, net::{TcpListener, TcpStream}, @@ -8,9 +6,9 @@ use std::{ time::Duration, }; -fn main() -> DResult<()> { +pub fn daemon() -> WResult<()> { let address = "0.0.0.0:21367"; - let listener = TcpListener::bind(address).map_err(DError::TcpBindError)?; + let listener = TcpListener::bind(address).map_err(WError::DaemonSocketBindError)?; println!("Listening for TCP packets at {}", address); for mut stream in listener.incoming() { match &mut stream { @@ -26,39 +24,43 @@ fn main() -> DResult<()> { Ok(()) } -fn handle_connection(stream: &mut TcpStream) -> DResult<()> { +fn handle_connection(stream: &mut TcpStream) -> WResult<()> { // TODO: Slowloris stream .set_read_timeout(Some(Duration::from_secs(5))) - .map_err(DError::ConnectionReadError)?; + .map_err(WError::DaemonConnectionReadError)?; let command = read_command(stream)?; match command.as_str() { "shutdown" => execute_shutdown_command(), - _ => Err(DError::InvalidRequest(command)), + _ => Err(WError::DaemonInvalidRequest(command)), } } -fn execute_shutdown_command() -> DResult<()> { +fn execute_shutdown_command() -> WResult<()> { let mut shutdown_cmd = build_shutdown_cmd(); shutdown_cmd.stdout(Stdio::piped()); shutdown_cmd.stderr(Stdio::piped()); - let output = shutdown_cmd.output().map_err(|e| DError::CommandError(e.to_string()))?; + let output = shutdown_cmd + .output() + .map_err(|e| WError::DaemonCommandError(e.to_string()))?; let status_code = output .status .code() - .ok_or(DError::CommandError("No exit code returned!".to_string()))?; + .ok_or(WError::DaemonCommandError("No exit code returned!".to_string()))?; if status_code != 0 { - return Err(DError::CommandExecutionError(output)); + return Err(WError::DaemonCommandExecutionError(output)); }; Ok(()) } -fn read_command(stream: &mut TcpStream) -> DResult { +fn read_command(stream: &mut TcpStream) -> WResult { let mut buf = Vec::new(); - let read = stream.read_to_end(&mut buf).map_err(DError::ConnectionReadError)?; + let read = stream + .read_to_end(&mut buf) + .map_err(WError::DaemonConnectionReadError)?; let command = String::from_utf8_lossy(&buf[..read]).to_string(); Ok(command) } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..8b25d2e --- /dev/null +++ b/src/error.rs @@ -0,0 +1,52 @@ +use std::fmt::Display; + +pub type WResult = std::result::Result; + +#[derive(Debug)] +pub enum WError { + // LIB + ParseMacError(String), + NotAMac(String), + NickNotAlphanumeric(String), + NickNotMapped(String), + InvalidConfigDir(String), + ConfigDirInitError(String), + ConfigFileFormatError(String), + UdpSocketBindError(String), + InvalidCommandString(String), + + DaemonSocketBindError(std::io::Error), + DaemonConnectionReadError(std::io::Error), + DaemonInvalidRequest(String), + DaemonCommandError(String), + DaemonCommandExecutionError(std::process::Output), +} + +impl Display for WError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let to_write = match self { + WError::ParseMacError(s) => format!("{}", s), + WError::NotAMac(s) => format!("'{}' is not a valid MAC", s), + WError::NickNotAlphanumeric(s) => format!("{}", s), + WError::NickNotMapped(s) => format!("{}", s), + WError::InvalidConfigDir(s) => format!("{}", s), + WError::ConfigDirInitError(s) => format!("{}", s), + WError::ConfigFileFormatError(s) => format!("{}", s), + WError::UdpSocketBindError(s) => format!("{}", s), + WError::InvalidCommandString(s) => format!("Command string '{}' is unknown", s), + + WError::DaemonCommandError(s) => format!("Failed to execute command {}", s), + WError::DaemonCommandExecutionError(output) => format!( + "\nExit Code : {}\nStdout : {}\nStderr : {}", + output.status.code().unwrap(), + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ), + WError::DaemonSocketBindError(e) => format!("Failed to create tcp listener {}", e), + WError::DaemonConnectionReadError(e) => format!("Error while reading from the socket {}", e), + WError::DaemonInvalidRequest(s) => format!("Command '{}' in request is invalid, dropping it", s), + }; + + write!(f, "{}", to_write) + } +} diff --git a/crates/wolz-lib/src/ifaddrs.rs b/src/ifaddrs.rs similarity index 100% rename from crates/wolz-lib/src/ifaddrs.rs rename to src/ifaddrs.rs diff --git a/crates/wolz-lib/src/mac.rs b/src/mac.rs similarity index 100% rename from crates/wolz-lib/src/mac.rs rename to src/mac.rs diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..3393453 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,71 @@ +mod config; +mod daemon; +mod error; +mod ifaddrs; +mod mac; +mod wolz; + +use error::WResult; +use std::env::args; +use wolz::Wolz; + +use crate::wolz::WCommand; + +fn main() -> WResult<()> { + let mut args = args(); + let arg_1 = args.next(); + let Some(arg_1) = arg_1 else { + return print_usage(); + }; + + match arg_1.as_str() { + "-h" | "--help" | "help" => { + return print_usage(); + } + _ => { + let command = args.next(); + let Some(command) = command else { + return print_usage(); + }; + + match command.as_str() { + "daemon" => daemon::daemon(), + _ => { + let arg = args.next(); + let Some(arg) = arg else { + return print_usage(); + }; + + let wolz = Wolz::create()?; + wolz.execute(&WCommand::create(command, arg)) + } + } + } + } +} + +fn print_usage() -> WResult<()> { + println!( + "{}", + r#"usage: woly ARG + +Options: + ARG Either the mac address of the device to send the wol magic + packet to, or a nickname specified in woly.conf to lookup + the mac. See details about config file below. + Eg: woly aa:bb:cc:dd:ee:ff + Eg: woly potato + +Config file: + Filename woly.conf + Location $WOLY_CONFIG_DIR/woly.conf, then $XDG_CONFIG_DIR/woly.conf, + then $HOME/woly.conf + Format KEY=VALUE + one per line without spaces, no comments allowed + Example potato=aa:bb:cc:dd:ee:ff + idly=12:34:23:e2:3d:ff +"# + ); + + Ok(()) +} diff --git a/crates/wolz-lib/src/wolz.rs b/src/wolz.rs similarity index 76% rename from crates/wolz-lib/src/wolz.rs rename to src/wolz.rs index 9f72fbd..80d5da5 100644 --- a/crates/wolz-lib/src/wolz.rs +++ b/src/wolz.rs @@ -1,22 +1,39 @@ use std::net::UdpSocket; use crate::{ - config::Config, - error::{WError, WResult}, - ifaddrs::{self}, + config::Config, daemon, error::{WError, WResult}, ifaddrs::{self}, }; pub struct Wolz { config: Config, } +pub struct WCommand { + name: String, + arg: String, +} + +impl WCommand { + pub fn create(name: String, arg: String) -> Self { + Self { name, arg } + } +} + impl Wolz { pub fn create() -> WResult { let config = Config::create()?; Ok(Self { config }) } - pub fn wake_up(&self, mac_or_nick: &str) -> WResult<()> { + pub fn execute(&self, command: &WCommand) -> WResult<()> { + match command.name.as_str() { + "up" => self.wake_up(&command.arg), + "down" => self.shut_down(&command.arg), + _ => Err(WError::InvalidCommandString(command.name.to_string())), + } + } + + fn wake_up(&self, mac_or_nick: &str) -> WResult<()> { let mac = self.config.get_mac(mac_or_nick)?; let mut magic_bytes = [0u8; 102]; mac.copy_magic_bytes(&mut magic_bytes); @@ -55,6 +72,10 @@ impl Wolz { Ok(()) } + + pub fn shut_down(&self, mac_or_nick: &str) -> WResult<()> { + todo!() + } } fn try_send_magic_bytes(magic_bytes: &[u8; 102], send_addr: &str, broadcast_addr: &str) -> WResult<()> {