mod config; use std::{ path::PathBuf, sync::{Arc, Mutex}, }; use clap::Args; use serde::{Deserialize, Serialize}; use crate::{server, signals, stdin}; use config::Config; #[derive(Args)] pub struct RunCli { #[command(flatten)] pub args: RunArgs, /// Don't actually run the server, but simply output the server configuration that would have /// been ran #[arg(short, long, default_value_t = false)] pub dry: bool, } #[derive(Args, Serialize, Deserialize, Clone)] pub struct RunArgs { /// Server jar to execute #[arg(long, value_name = "JAR_PATH")] #[serde(skip_serializing_if = "::std::option::Option::is_none")] pub jar: Option, /// Java command to run the server jar with #[arg(long, value_name = "JAVA_CMD")] #[serde(skip_serializing_if = "::std::option::Option::is_none")] pub java: Option, /// XMS value in megabytes for the server instance #[arg(long)] #[serde(skip_serializing_if = "::std::option::Option::is_none")] pub xms: Option, /// XMX value in megabytes for the server instance #[arg(long)] #[serde(skip_serializing_if = "::std::option::Option::is_none")] pub xmx: Option, } fn backups_thread(counter: Arc>) { loop { let next_scheduled_time = { let server = counter.lock().unwrap(); server.backups.next_scheduled_time().unwrap() }; let now = chrono::offset::Utc::now(); if next_scheduled_time > now { std::thread::sleep((next_scheduled_time - now).to_std().unwrap()); } { let mut server = counter.lock().unwrap(); // We explicitely ignore the error here, as we don't want the thread to fail let _ = server.backup(); } } } impl RunCli { pub fn run(&self, cli: &super::Cli, global: &super::Config) -> crate::Result<()> { let config: Config = cli.config(&self.args)?; println!("{:?}", config); let (_, mut signals) = signals::install_signal_handlers()?; let mut cmd = server::ServerCommand::new(global.server, &global.server_version) .java(&config.java) .jar(config.jar.clone()) .config(global.config.clone()) .world(global.world.clone()) .backup(global.backup.clone()) .managers(global.layers.clone()) .xms(config.xms) .xmx(config.xmx); cmd.canonicalize()?; if self.dry { print!("{}", cmd); return Ok(()); } let counter = Arc::new(Mutex::new(cmd.spawn()?)); if !global.layers.is_empty() { let clone = Arc::clone(&counter); std::thread::spawn(move || backups_thread(clone)); } // Spawn thread that handles the main stdin loop let clone = Arc::clone(&counter); std::thread::spawn(move || stdin::handle_stdin(clone)); // Signal handler loop exits the process when necessary Ok(signals::handle_signals(&mut signals, counter)?) } }