single tool
This commit is contained in:
Generated
-11
@@ -5,14 +5,3 @@ version = 4
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "wolz"
|
name = "wolz"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
|
||||||
"wolz-lib",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wolz-lib"
|
|
||||||
version = "0.0.1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wolzd"
|
|
||||||
version = "0.0.1"
|
|
||||||
|
|||||||
+4
-5
@@ -1,5 +1,4 @@
|
|||||||
[workspace]
|
[package]
|
||||||
members = [
|
name = "wolz"
|
||||||
"crates/*",
|
version = "0.0.1"
|
||||||
]
|
edition = "2024"
|
||||||
resolver = "2"
|
|
||||||
|
|||||||
Generated
-7
@@ -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"
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "wolz"
|
|
||||||
version = "0.0.1"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
wolz-lib = { path = "../wolz-lib" }
|
|
||||||
@@ -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
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "wolzd"
|
|
||||||
version = "0.0.1"
|
|
||||||
edition = "2024"
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
use std::{fmt::Display, process::Output};
|
|
||||||
|
|
||||||
pub type DResult<T> = Result<T, DError>;
|
|
||||||
|
|
||||||
#[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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "wolz-lib"
|
|
||||||
version = "0.0.1"
|
|
||||||
edition = "2024"
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
pub type WResult<T> = std::result::Result<T, WError>;
|
|
||||||
|
|
||||||
#[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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
pub mod error;
|
|
||||||
pub(crate) mod mac;
|
|
||||||
pub(crate) mod config;
|
|
||||||
mod ifaddrs;
|
|
||||||
pub mod wolz;
|
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
mod error;
|
use crate::error::{WError, WResult};
|
||||||
|
|
||||||
use crate::error::{DError, DResult};
|
|
||||||
use std::{
|
use std::{
|
||||||
io::Read,
|
io::Read,
|
||||||
net::{TcpListener, TcpStream},
|
net::{TcpListener, TcpStream},
|
||||||
@@ -8,9 +6,9 @@ use std::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn main() -> DResult<()> {
|
pub fn daemon() -> WResult<()> {
|
||||||
let address = "0.0.0.0:21367";
|
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);
|
println!("Listening for TCP packets at {}", address);
|
||||||
for mut stream in listener.incoming() {
|
for mut stream in listener.incoming() {
|
||||||
match &mut stream {
|
match &mut stream {
|
||||||
@@ -26,39 +24,43 @@ fn main() -> DResult<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_connection(stream: &mut TcpStream) -> DResult<()> {
|
fn handle_connection(stream: &mut TcpStream) -> WResult<()> {
|
||||||
// TODO: Slowloris
|
// TODO: Slowloris
|
||||||
stream
|
stream
|
||||||
.set_read_timeout(Some(Duration::from_secs(5)))
|
.set_read_timeout(Some(Duration::from_secs(5)))
|
||||||
.map_err(DError::ConnectionReadError)?;
|
.map_err(WError::DaemonConnectionReadError)?;
|
||||||
let command = read_command(stream)?;
|
let command = read_command(stream)?;
|
||||||
match command.as_str() {
|
match command.as_str() {
|
||||||
"shutdown" => execute_shutdown_command(),
|
"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();
|
let mut shutdown_cmd = build_shutdown_cmd();
|
||||||
shutdown_cmd.stdout(Stdio::piped());
|
shutdown_cmd.stdout(Stdio::piped());
|
||||||
shutdown_cmd.stderr(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
|
let status_code = output
|
||||||
.status
|
.status
|
||||||
.code()
|
.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 {
|
if status_code != 0 {
|
||||||
return Err(DError::CommandExecutionError(output));
|
return Err(WError::DaemonCommandExecutionError(output));
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_command(stream: &mut TcpStream) -> DResult<String> {
|
fn read_command(stream: &mut TcpStream) -> WResult<String> {
|
||||||
let mut buf = Vec::new();
|
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();
|
let command = String::from_utf8_lossy(&buf[..read]).to_string();
|
||||||
Ok(command)
|
Ok(command)
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
pub type WResult<T> = std::result::Result<T, WError>;
|
||||||
|
|
||||||
|
#[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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+71
@@ -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(())
|
||||||
|
}
|
||||||
@@ -1,22 +1,39 @@
|
|||||||
use std::net::UdpSocket;
|
use std::net::UdpSocket;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::Config,
|
config::Config, daemon, error::{WError, WResult}, ifaddrs::{self},
|
||||||
error::{WError, WResult},
|
|
||||||
ifaddrs::{self},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Wolz {
|
pub struct Wolz {
|
||||||
config: Config,
|
config: Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct WCommand {
|
||||||
|
name: String,
|
||||||
|
arg: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WCommand {
|
||||||
|
pub fn create(name: String, arg: String) -> Self {
|
||||||
|
Self { name, arg }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Wolz {
|
impl Wolz {
|
||||||
pub fn create() -> WResult<Self> {
|
pub fn create() -> WResult<Self> {
|
||||||
let config = Config::create()?;
|
let config = Config::create()?;
|
||||||
Ok(Self { config })
|
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 mac = self.config.get_mac(mac_or_nick)?;
|
||||||
let mut magic_bytes = [0u8; 102];
|
let mut magic_bytes = [0u8; 102];
|
||||||
mac.copy_magic_bytes(&mut magic_bytes);
|
mac.copy_magic_bytes(&mut magic_bytes);
|
||||||
@@ -55,6 +72,10 @@ impl Wolz {
|
|||||||
|
|
||||||
Ok(())
|
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<()> {
|
fn try_send_magic_bytes(magic_bytes: &[u8; 102], send_addr: &str, broadcast_addr: &str) -> WResult<()> {
|
||||||
Reference in New Issue
Block a user