From 0d9af8f605139583b7a7d2b5e8a513f0691a96d3 Mon Sep 17 00:00:00 2001 From: cool-mist Date: Thu, 9 Jul 2026 17:22:32 +0530 Subject: [PATCH] shutdown WIP --- Cargo.lock | 4 ++ crates/wolz-daemon/Cargo.toml | 4 ++ crates/wolz-daemon/src/error.rs | 28 +++++++++++++ crates/wolz-daemon/src/main.rs | 72 +++++++++++++++++++++++++++++++++ crates/wolz-lib/src/error.rs | 4 +- crates/wolz-lib/src/mac.rs | 7 ++++ crates/wolz-lib/src/wolz.rs | 9 +++-- 7 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 crates/wolz-daemon/Cargo.toml create mode 100644 crates/wolz-daemon/src/error.rs create mode 100644 crates/wolz-daemon/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 9c22eb4..daea3f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,3 +12,7 @@ dependencies = [ [[package]] name = "wolz-lib" version = "0.0.1" + +[[package]] +name = "wolzd" +version = "0.0.1" diff --git a/crates/wolz-daemon/Cargo.toml b/crates/wolz-daemon/Cargo.toml new file mode 100644 index 0000000..8fbb7e0 --- /dev/null +++ b/crates/wolz-daemon/Cargo.toml @@ -0,0 +1,4 @@ +[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 new file mode 100644 index 0000000..1510eb9 --- /dev/null +++ b/crates/wolz-daemon/src/error.rs @@ -0,0 +1,28 @@ +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-daemon/src/main.rs b/crates/wolz-daemon/src/main.rs new file mode 100644 index 0000000..593b2df --- /dev/null +++ b/crates/wolz-daemon/src/main.rs @@ -0,0 +1,72 @@ +mod error; + +use crate::error::{DError, DResult}; +use std::{ + io::Read, + net::{TcpListener, TcpStream}, + process::{Command, Stdio}, + time::Duration, +}; + +fn main() -> DResult<()> { + let address = "0.0.0.0:21367"; + let listener = TcpListener::bind(address).map_err(DError::TcpBindError)?; + println!("Listening on {}", address); + for mut stream in listener.incoming() { + match &mut stream { + Ok(stream) => { + if let Err(e) = handle_connection(stream) { + eprintln!("Failed to handle request {}", e); + } + } + Err(e) => eprintln!("{}", e), + } + } + + Ok(()) +} + +fn handle_connection(stream: &mut TcpStream) -> DResult<()> { + // TODO: Slowloris + stream + .set_read_timeout(Some(Duration::from_secs(5))) + .map_err(DError::ConnectionReadError)?; + let command = read_command(stream)?; + match command.as_str() { + "shutdown" => execute_shutdown_command(), + _ => Err(DError::InvalidRequest(command)), + } +} + +fn execute_shutdown_command() -> DResult<()> { + 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 status_code = output + .status + .code() + .ok_or(DError::CommandError("No exit code returned!".to_string()))?; + + if status_code != 0 { + return Err(DError::CommandExecutionError(output)); + }; + + Ok(()) +} + +fn read_command(stream: &mut TcpStream) -> DResult { + let mut buf = Vec::new(); + let read = stream.read_to_end(&mut buf).map_err(DError::ConnectionReadError)?; + let command = String::from_utf8_lossy(&buf[..read]).to_string(); + Ok(command) +} + +fn build_shutdown_cmd() -> Command { + let mut cmd = Command::new("systemctl"); + cmd.arg("-k"); + cmd.arg("poweroff"); + + cmd +} diff --git a/crates/wolz-lib/src/error.rs b/crates/wolz-lib/src/error.rs index 20fccab..90c4723 100644 --- a/crates/wolz-lib/src/error.rs +++ b/crates/wolz-lib/src/error.rs @@ -11,7 +11,7 @@ pub enum WError { InvalidConfigDir(String), ConfigDirInitError(String), ConfigFileFormatError(String), - UdpSocketError(String), + UdpSocketBindError(String), } impl Display for WError { @@ -24,7 +24,7 @@ impl Display for WError { WError::InvalidConfigDir(s) => format!("{}", s), WError::ConfigDirInitError(s) => format!("{}", s), WError::ConfigFileFormatError(s) => format!("{}", s), - WError::UdpSocketError(s) => format!("{}", s), + WError::UdpSocketBindError(s) => format!("{}", s), }; write!(f, "{}", to_write) diff --git a/crates/wolz-lib/src/mac.rs b/crates/wolz-lib/src/mac.rs index a2a756e..b45da63 100644 --- a/crates/wolz-lib/src/mac.rs +++ b/crates/wolz-lib/src/mac.rs @@ -68,9 +68,16 @@ impl Nick { } } +pub(crate) struct IpV4 { + original_string: String, + ip: [u8; 4], + port: u32, +} + pub(crate) enum Device { Mac(Mac), Nick(Nick), + IpV4(IpV4), } impl Device { diff --git a/crates/wolz-lib/src/wolz.rs b/crates/wolz-lib/src/wolz.rs index 7c82afe..b93c784 100644 --- a/crates/wolz-lib/src/wolz.rs +++ b/crates/wolz-lib/src/wolz.rs @@ -21,13 +21,16 @@ impl Wolz { mac.copy_magic_bytes(&mut magic_bytes); let socket = UdpSocket::bind("0.0.0.0:0") - .map_err(|e| WError::UdpSocketError(format!("couldn't bind to socket because {}", e)))?; + .map_err(|e| WError::UdpSocketBindError(format!("couldn't bind to socket because {}", e)))?; socket.set_broadcast(true) - .map_err(|e| WError::UdpSocketError(format!("couldn't set broadcast because {}", e)))?; + .map_err(|e| WError::UdpSocketBindError(format!("couldn't set broadcast because {}", e)))?; socket .send_to(&magic_bytes, "255.255.255.255:9") - .map_err(|e| WError::UdpSocketError(format!("couldn't send data because {}", e)))?; + .map_err(|e| WError::UdpSocketBindError(format!("couldn't send data because {}", e)))?; println!("Magic packet sent to 255.255.255.255:9 for Mac {:}", mac); Ok(()) } + + pub fn shutdown(&self, ip_or_nick: &str) -> WResult<()> { + } }