From db05108dfaa14044c187d45fc6c9fc479d82b6d0 Mon Sep 17 00:00:00 2001 From: yuzu Date: Wed, 9 Jul 2025 05:14:23 +0000 Subject: reorganize git-svn-id: svn+ssh://diminuette.aengel.lesbianunix.dev/salaryman/trunk@11 b9215c17-b818-4693-b096-d1e41a411fef --- src/bin/smd.rs | 92 ------------------- src/lib.rs | 2 +- src/model.rs | 253 --------------------------------------------------- src/service.rs | 253 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/smd/endpoints.rs | 1 + src/smd/main.rs | 95 +++++++++++++++++++ 6 files changed, 350 insertions(+), 346 deletions(-) delete mode 100644 src/bin/smd.rs delete mode 100644 src/model.rs create mode 100644 src/service.rs create mode 100644 src/smd/endpoints.rs create mode 100644 src/smd/main.rs (limited to 'src') diff --git a/src/bin/smd.rs b/src/bin/smd.rs deleted file mode 100644 index 5a8401a..0000000 --- a/src/bin/smd.rs +++ /dev/null @@ -1,92 +0,0 @@ -use clap::Parser; -use serde::{Deserialize, Serialize}; - -use tokio::fs::read_to_string; - -use std::{net::IpAddr, path::PathBuf}; - -use salaryman::model::{Service, ServiceConf}; - -#[derive(Parser, Debug)] -#[command(version, about, long_about = None)] -struct Args { - #[arg( - short, - long, - value_name = "FILE", - help = "config file override", - default_value = "salaryman.toml" - )] - config: PathBuf, - #[arg( - short, - long, - value_name = "ADDR", - help = "IP address to bind API to", - default_value = "127.0.0.1" - )] - address: IpAddr, - #[arg( - short, - long, - value_name = "PORT", - help = "TCP Port to bind API to", - default_value = "3080" - )] - port: u16, -} - -#[derive(Serialize, Deserialize, Debug)] -struct Config { - address: Option, - port: Option, - service: Vec, -} -impl Config { - fn new() -> Self { - Self { - address: None, - port: None, - service: Vec::new(), - } - } -} - -async fn load_config(file: &PathBuf) -> Config { - let s: String = match read_to_string(file).await { - Ok(s) => s, - Err(_) => String::new(), - }; - match toml::from_str(s.as_str()) { - Ok(c) => c, - Err(_) => Config::new(), - } -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - let args = Args::parse(); - let conf: Config = load_config(&args.config).await; - let mut services: Vec = Vec::new(); - for i in 0..conf.service.len() { - services.push(Service::from_conf(&conf.service[i])); - if conf.service[i].autostart { - services[i].start().await?; - services[i].scan_stdout().await?; - services[i].scan_stderr().await?; - } - } - tokio::time::sleep(std::time::Duration::from_secs(60)).await; - println!("trying to write to stdin!"); - for i in 0..services.len() { - services[i].write_stdin("stop\n".into()).await?; - } - tokio::time::sleep(std::time::Duration::from_secs(30)).await; - for mut service in services { - match service.stop().await { - Ok(_) => println!("lol it was killed"), - Err(_) => println!("it either didn't exist, or failed to kill"), - } - } - Ok(()) -} diff --git a/src/lib.rs b/src/lib.rs index 65880be..1f278a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1 @@ -pub mod model; +pub mod service; diff --git a/src/model.rs b/src/model.rs deleted file mode 100644 index 60960e3..0000000 --- a/src/model.rs +++ /dev/null @@ -1,253 +0,0 @@ -use serde::{Deserialize, Serialize}; -use tokio::{ - io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, - process::{Child, Command}, - sync::{ - Mutex, - mpsc::{Receiver, channel}, - }, - task::spawn, -}; - -use std::{path::PathBuf, process::Stdio, sync::Arc}; - -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct ServiceConf { - pub name: String, - pub command: String, - pub args: Option, - pub directory: Option, - pub autostart: bool, -} -impl Default for ServiceConf { - fn default() -> Self { - Self::new() - } -} -impl ServiceConf { - /** - * Returns a new empty `ServiceConf` - */ - pub fn new() -> Self { - Self { - name: String::new(), - command: String::new(), - args: None, - directory: None, - autostart: false, - } - } - /** - * Returns a new `ServiceConf` from parts. - */ - pub fn from_parts( - name: String, - command: String, - args: Option, - directory: Option, - autostart: bool, - ) -> Self { - Self { - name, - command, - args, - directory, - autostart, - } - } -} - -#[derive(Debug)] -pub struct Service { - conf: ServiceConf, - proc: Option>>, - pub stdout: Option>>>, - pub stderr: Option>>>, -} -impl Default for Service { - fn default() -> Self { - Self::new() - } -} -impl Service { - /** - * Returns a new empty `Service` - */ - pub fn new() -> Self { - Self { - conf: ServiceConf::default(), - proc: None, - stdout: None, - stderr: None, - } - } - /** - * Returns a `Service` made from a `ServiceConf`. - */ - pub fn from_conf(conf: &ServiceConf) -> Self { - Self { - conf: conf.clone(), - proc: None, - stdout: None, - stderr: None, - } - } - /** - * Returns the name of the service - */ - pub async fn name(&self) -> &str { - &self.conf.name - } - /** - * Uses `tokio::process::Command` to start the service. - */ - pub async fn start(&mut self) -> Result<(), Box> { - if self.proc.is_some() { - return Err(Box::new(std::io::Error::new( - std::io::ErrorKind::AlreadyExists, - "Process Already Exists", - ))); - } - let cmd = &self.conf.command; - let mut proc = Command::new(cmd); - proc.stdin(Stdio::piped()); - proc.stdout(Stdio::piped()); - proc.stderr(Stdio::piped()); - if let Some(a) = &self.conf.args { - proc.args(a.split_whitespace()); - }; - if let Some(c) = &self.conf.directory { - proc.current_dir(c); - }; - let child = proc.spawn()?; - self.proc = Some(Arc::new(Mutex::new(child))); - Ok(()) - } - /** - * Returns true when process is started and false when process is stopped. - */ - pub async fn started(&self) -> bool { - self.proc.is_some() - } - /** - * Invokes kill on the service process - */ - pub async fn stop(&mut self) -> Result<(), Box> { - if let Some(proc) = self.proc.clone() { - let mut lock = proc.lock().await; - lock.kill().await?; - drop(lock); - self.proc = None; - Ok(()) - } else { - Err(Box::new(std::io::Error::new( - std::io::ErrorKind::NotFound, - "No Process Associated with Service", - ))) - } - } - /** - * Restarts service process - */ - pub async fn restart(&mut self) -> Result<(), Box> { - self.stop().await?; - self.start().await?; - Ok(()) - } - /** - * Takes control of service process' stdout file handle and spawns a new task to continuously - * scan it. - */ - pub async fn scan_stdout(&mut self) -> Result<(), Box> { - if let Some(proc) = self.proc.clone() { - let mut lock = proc.lock().await; - let stdout = if let Some(stdout) = lock.stdout.take() { - stdout - } else { - return Err(Box::new(std::io::Error::new( - std::io::ErrorKind::NotFound, - "No stdout handle associated with process", - ))); - }; - drop(lock); - let (tx, rx) = channel(100); - let sname = self.conf.name.clone(); - spawn(async move { - let mut br = BufReader::new(stdout).lines(); - while let Ok(Some(line)) = br.next_line().await { - println!("{} :: {}", &sname, &line); - if let Err(_) = tx.send(line).await { - return; - }; - } - }); - self.stdout = Some(Arc::new(Mutex::new(rx))); - Ok(()) - } else { - Err(Box::new(std::io::Error::new( - std::io::ErrorKind::NotFound, - "No Process Associated with Service", - ))) - } - } - /** - * Takes control of service process' stderr file handle and spawns a new task to continuously - * scan it. - */ - pub async fn scan_stderr(&mut self) -> Result<(), Box> { - if let Some(proc) = self.proc.clone() { - let mut lock = proc.lock().await; - let stderr = if let Some(stderr) = lock.stderr.take() { - stderr - } else { - return Err(Box::new(std::io::Error::new( - std::io::ErrorKind::NotFound, - "No stderr handle associated with process", - ))); - }; - drop(lock); - let (tx, rx) = channel(100); - let sname = self.conf.name.clone(); - spawn(async move { - let mut br = BufReader::new(stderr).lines(); - while let Ok(Some(line)) = br.next_line().await { - eprintln!("{} :: {}", &sname, &line); - if let Err(_) = tx.send(line).await { - return; - }; - } - }); - self.stderr = Some(Arc::new(Mutex::new(rx))); - Ok(()) - } else { - Err(Box::new(std::io::Error::new( - std::io::ErrorKind::NotFound, - "No Process Associated with Service", - ))) - } - } - /** - * Writes to the service process' stdin, if it exists. - */ - pub async fn write_stdin(&mut self, buf: String) -> Result<(), Box> { - if let Some(proc) = self.proc.clone() { - let mut lock = proc.lock().await; - let stdin = if let Some(stdin) = lock.stdin.as_mut() { - stdin - } else { - return Err(Box::new(std::io::Error::new( - std::io::ErrorKind::NotFound, - "No stdin handle associated with process", - ))); - }; - stdin.write(&buf.as_bytes()).await?; - stdin.flush().await?; - Ok(()) - } else { - Err(Box::new(std::io::Error::new( - std::io::ErrorKind::NotFound, - "No Process Associated with Service", - ))) - } - } -} diff --git a/src/service.rs b/src/service.rs new file mode 100644 index 0000000..60960e3 --- /dev/null +++ b/src/service.rs @@ -0,0 +1,253 @@ +use serde::{Deserialize, Serialize}; +use tokio::{ + io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, + process::{Child, Command}, + sync::{ + Mutex, + mpsc::{Receiver, channel}, + }, + task::spawn, +}; + +use std::{path::PathBuf, process::Stdio, sync::Arc}; + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct ServiceConf { + pub name: String, + pub command: String, + pub args: Option, + pub directory: Option, + pub autostart: bool, +} +impl Default for ServiceConf { + fn default() -> Self { + Self::new() + } +} +impl ServiceConf { + /** + * Returns a new empty `ServiceConf` + */ + pub fn new() -> Self { + Self { + name: String::new(), + command: String::new(), + args: None, + directory: None, + autostart: false, + } + } + /** + * Returns a new `ServiceConf` from parts. + */ + pub fn from_parts( + name: String, + command: String, + args: Option, + directory: Option, + autostart: bool, + ) -> Self { + Self { + name, + command, + args, + directory, + autostart, + } + } +} + +#[derive(Debug)] +pub struct Service { + conf: ServiceConf, + proc: Option>>, + pub stdout: Option>>>, + pub stderr: Option>>>, +} +impl Default for Service { + fn default() -> Self { + Self::new() + } +} +impl Service { + /** + * Returns a new empty `Service` + */ + pub fn new() -> Self { + Self { + conf: ServiceConf::default(), + proc: None, + stdout: None, + stderr: None, + } + } + /** + * Returns a `Service` made from a `ServiceConf`. + */ + pub fn from_conf(conf: &ServiceConf) -> Self { + Self { + conf: conf.clone(), + proc: None, + stdout: None, + stderr: None, + } + } + /** + * Returns the name of the service + */ + pub async fn name(&self) -> &str { + &self.conf.name + } + /** + * Uses `tokio::process::Command` to start the service. + */ + pub async fn start(&mut self) -> Result<(), Box> { + if self.proc.is_some() { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::AlreadyExists, + "Process Already Exists", + ))); + } + let cmd = &self.conf.command; + let mut proc = Command::new(cmd); + proc.stdin(Stdio::piped()); + proc.stdout(Stdio::piped()); + proc.stderr(Stdio::piped()); + if let Some(a) = &self.conf.args { + proc.args(a.split_whitespace()); + }; + if let Some(c) = &self.conf.directory { + proc.current_dir(c); + }; + let child = proc.spawn()?; + self.proc = Some(Arc::new(Mutex::new(child))); + Ok(()) + } + /** + * Returns true when process is started and false when process is stopped. + */ + pub async fn started(&self) -> bool { + self.proc.is_some() + } + /** + * Invokes kill on the service process + */ + pub async fn stop(&mut self) -> Result<(), Box> { + if let Some(proc) = self.proc.clone() { + let mut lock = proc.lock().await; + lock.kill().await?; + drop(lock); + self.proc = None; + Ok(()) + } else { + Err(Box::new(std::io::Error::new( + std::io::ErrorKind::NotFound, + "No Process Associated with Service", + ))) + } + } + /** + * Restarts service process + */ + pub async fn restart(&mut self) -> Result<(), Box> { + self.stop().await?; + self.start().await?; + Ok(()) + } + /** + * Takes control of service process' stdout file handle and spawns a new task to continuously + * scan it. + */ + pub async fn scan_stdout(&mut self) -> Result<(), Box> { + if let Some(proc) = self.proc.clone() { + let mut lock = proc.lock().await; + let stdout = if let Some(stdout) = lock.stdout.take() { + stdout + } else { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::NotFound, + "No stdout handle associated with process", + ))); + }; + drop(lock); + let (tx, rx) = channel(100); + let sname = self.conf.name.clone(); + spawn(async move { + let mut br = BufReader::new(stdout).lines(); + while let Ok(Some(line)) = br.next_line().await { + println!("{} :: {}", &sname, &line); + if let Err(_) = tx.send(line).await { + return; + }; + } + }); + self.stdout = Some(Arc::new(Mutex::new(rx))); + Ok(()) + } else { + Err(Box::new(std::io::Error::new( + std::io::ErrorKind::NotFound, + "No Process Associated with Service", + ))) + } + } + /** + * Takes control of service process' stderr file handle and spawns a new task to continuously + * scan it. + */ + pub async fn scan_stderr(&mut self) -> Result<(), Box> { + if let Some(proc) = self.proc.clone() { + let mut lock = proc.lock().await; + let stderr = if let Some(stderr) = lock.stderr.take() { + stderr + } else { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::NotFound, + "No stderr handle associated with process", + ))); + }; + drop(lock); + let (tx, rx) = channel(100); + let sname = self.conf.name.clone(); + spawn(async move { + let mut br = BufReader::new(stderr).lines(); + while let Ok(Some(line)) = br.next_line().await { + eprintln!("{} :: {}", &sname, &line); + if let Err(_) = tx.send(line).await { + return; + }; + } + }); + self.stderr = Some(Arc::new(Mutex::new(rx))); + Ok(()) + } else { + Err(Box::new(std::io::Error::new( + std::io::ErrorKind::NotFound, + "No Process Associated with Service", + ))) + } + } + /** + * Writes to the service process' stdin, if it exists. + */ + pub async fn write_stdin(&mut self, buf: String) -> Result<(), Box> { + if let Some(proc) = self.proc.clone() { + let mut lock = proc.lock().await; + let stdin = if let Some(stdin) = lock.stdin.as_mut() { + stdin + } else { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::NotFound, + "No stdin handle associated with process", + ))); + }; + stdin.write(&buf.as_bytes()).await?; + stdin.flush().await?; + Ok(()) + } else { + Err(Box::new(std::io::Error::new( + std::io::ErrorKind::NotFound, + "No Process Associated with Service", + ))) + } + } +} diff --git a/src/smd/endpoints.rs b/src/smd/endpoints.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/smd/endpoints.rs @@ -0,0 +1 @@ + diff --git a/src/smd/main.rs b/src/smd/main.rs new file mode 100644 index 0000000..231701b --- /dev/null +++ b/src/smd/main.rs @@ -0,0 +1,95 @@ +mod endpoints; + +use clap::Parser; +use serde::{Deserialize, Serialize}; +use tokio::fs::read_to_string; +use salaryman::service::{Service, ServiceConf}; + +use std::{net::IpAddr, path::PathBuf}; + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct Args { + #[arg( + short, + long, + value_name = "FILE", + help = "config file override", + default_value = "salaryman.toml" + )] + config: PathBuf, + #[arg( + short, + long, + value_name = "ADDR", + help = "IP address to bind API to", + default_value = "127.0.0.1" + )] + address: IpAddr, + #[arg( + short, + long, + value_name = "PORT", + help = "TCP Port to bind API to", + default_value = "3080" + )] + port: u16, +} + +#[derive(Serialize, Deserialize, Debug)] +struct Config { + address: Option, + port: Option, + service: Vec, +} +/* +impl Config { + fn new() -> Self { + Self { + address: None, + port: None, + service: Vec::new(), + } + } +} +*/ + +async fn load_config(file: &PathBuf) -> Result> { + let s: String = match read_to_string(file).await { + Ok(s) => s, + Err(_) => return Err(Box::new(std::io::Error::new(std::io::ErrorKind::NotFound, "cannot find config file"))), + }; + match toml::from_str(s.as_str()) { + Ok(c) => Ok(c), + Err(_) => Err(Box::new(std::io::Error::new(std::io::ErrorKind::Other, "unable to parse config file"))), + } +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let args = Args::parse(); + let conf: Config = load_config(&args.config).await?; + let mut services: Vec = Vec::new(); + for i in 0..conf.service.len() { + services.push(Service::from_conf(&conf.service[i])); + if conf.service[i].autostart { + services[i].start().await?; + services[i].scan_stdout().await?; + services[i].scan_stderr().await?; + } + } + tokio::time::sleep(std::time::Duration::from_secs(60)).await; + println!("trying to write to stdin!"); + for i in 0..services.len() { + services[i].write_stdin("stop\n".into()).await?; + } + tokio::time::sleep(std::time::Duration::from_secs(30)).await; + for mut service in services { + match service.stop().await { + Ok(_) => println!("lol it was killed"), + Err(_) => println!("it either didn't exist, or failed to kill"), + } + } + Ok(()) +} + -- cgit 1.4.1-2-gfad0