diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 1917669..0000000 --- a/.dockerignore +++ /dev/null @@ -1,5 +0,0 @@ -* - -!Cargo.toml -!Cargo.lock -!src/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 4339669..6b969cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,14 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://git.rustybever.be/Chewing_Bever/alex/src/branch/dev) -### Added - -* Rudimentary signal handling for gently stopping server - * A single stop signal will trigger the Java process to shut down, but Alex - still expects to be run from a utility such as dumb-init -* Properly back up entire config directory -* Inject Java optimisation flags - ## [0.1.1](https://git.rustybever.be/Chewing_Bever/alex/src/tag/0.1.1) ### Changed diff --git a/Cargo.lock b/Cargo.lock index 5124e00..5953204 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,7 +15,6 @@ dependencies = [ "chrono", "clap", "flate2", - "signal-hook", "tar", ] @@ -384,25 +383,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "signal-hook" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - [[package]] name = "strsim" version = "0.10.0" diff --git a/Cargo.toml b/Cargo.toml index 4c0ba69..dd2f79b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,6 @@ flate2 = "1.0.26" # Used for backup filenames chrono = "0.4.26" clap = { version = "4.3.1", features = ["derive", "env"] } -signal-hook = "0.3.15" [profile.release] lto = "fat" diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index cb3b9e0..0000000 --- a/Dockerfile +++ /dev/null @@ -1,64 +0,0 @@ -FROM rust:1.70-alpine3.18 AS builder - -ARG DI_VER=1.2.5 - -WORKDIR /app - -COPY . ./ - -RUN apk add --no-cache build-base unzip curl && \ - curl -Lo - "https://github.com/Yelp/dumb-init/archive/refs/tags/v${DI_VER}.tar.gz" | tar -xzf - && \ - cd "dumb-init-${DI_VER}" && \ - make SHELL=/bin/sh && \ - mv dumb-init .. - -RUN cargo build && \ - [ "$(readelf -d target/debug/alex | grep NEEDED | wc -l)" = 0 ] - - -# We use ${:-} instead of a default value because the argument is always passed -# to the build, it'll just be blank most likely -FROM eclipse-temurin:17-jre-alpine - -# Build arguments -ARG MC_VERSION=1.19.4 -ARG PAPERMC_VERSION=545 - -RUN addgroup -Sg 1000 paper && \ - adduser -SHG paper -u 1000 paper - -# Create worlds and config directory -WORKDIR /app -RUN mkdir -p worlds config/cache backups - -# Download server file -ADD "https://papermc.io/api/v2/projects/paper/versions/$MC_VERSION/builds/$PAPERMC_VERSION/downloads/paper-$MC_VERSION-$PAPERMC_VERSION.jar" server.jar - -# Make sure the server user can access all necessary folders -RUN chown -R paper:paper /app - -# Store the cache in an anonymous volume, which means it won't get stored in the other volumes -VOLUME /app/config/cache -VOLUME /app/backups - -COPY --from=builder /app/dumb-init /bin/dumb-init -COPY --from=builder /app/target/debug/alex /bin/alex - -RUN chmod +x /bin/alex - -# Default value to keep users from eating up all ram accidentally -ENV ALEX_XMS=1024 \ - ALEX_XMX=2048 \ - ALEX_JAR=/app/server.jar \ - ALEX_CONFIG_DIR=/app/config \ - ALEX_WORLD_DIR=/app/worlds \ - ALEX_BACKUPS_DIR=/app/backups \ - ALEX_SERVER_VERSION="${MC_VERSION}-${PAPERMC_VERSION}" - -# Document exposed ports -EXPOSE 25565 - -# Switch to non-root user -USER paper:paper - -ENTRYPOINT ["/bin/alex", "paper"] diff --git a/src/main.rs b/src/main.rs index 9f993a9..7837887 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,4 @@ mod server; -mod signals; -mod stdin; use clap::Parser; use server::ServerType; @@ -81,8 +79,7 @@ fn backups_thread(counter: Arc>, frequency: u64) { } } -fn main() -> io::Result<()> { - let (_, mut signals) = signals::install_signal_handlers()?; +fn main() { let cli = Cli::parse(); let cmd = server::ServerCommand::new(cli.type_, &cli.server_version) @@ -94,17 +91,33 @@ fn main() -> io::Result<()> { .xms(cli.xms) .xmx(cli.xmx) .max_backups(cli.max_backups); - let counter = Arc::new(Mutex::new(cmd.spawn()?)); + let counter = Arc::new(Mutex::new(cmd.spawn().expect("Failed to start server."))); if cli.frequency > 0 { let clone = Arc::clone(&counter); std::thread::spawn(move || backups_thread(clone, cli.frequency)); } - // Spawn thread that handles the main stdin loop - let clone = Arc::clone(&counter); - std::thread::spawn(move || stdin::handle_stdin(clone)); + let stdin = io::stdin(); + let input = &mut String::new(); - // Signal handler loop exits the process when necessary - signals::handle_signals(&mut signals, counter) + loop { + input.clear(); + + if stdin.read_line(input).is_err() { + continue; + }; + + { + let mut server = counter.lock().unwrap(); + + if let Err(e) = server.send_command(input) { + println!("{}", e); + }; + } + + if input.trim() == "stop" { + break; + } + } } diff --git a/src/server/process.rs b/src/server/process.rs index a9a9f45..5640b18 100644 --- a/src/server/process.rs +++ b/src/server/process.rs @@ -67,10 +67,6 @@ impl ServerProcess { Ok(()) } - pub fn kill(&mut self) -> std::io::Result<()> { - self.child.kill() - } - /// Perform a backup by disabling the server's save feature and flushing its data, before /// creating an archive file. pub fn backup(&mut self) -> std::io::Result<()> { @@ -93,11 +89,7 @@ impl ServerProcess { // The server's save feature needs to be enabled again even if the archive failed to create self.custom("save-on")?; - if res.is_ok() { - self.custom("say server backed up successfully")?; - } else { - self.custom("an error occured while backing up the server")?; - } + self.custom("say server backed up successfully")?; res } diff --git a/src/signals.rs b/src/signals.rs deleted file mode 100644 index 61c38f9..0000000 --- a/src/signals.rs +++ /dev/null @@ -1,68 +0,0 @@ -use std::io; -use std::sync::atomic::AtomicBool; -use std::sync::{Arc, Mutex}; - -use signal_hook::consts::TERM_SIGNALS; -use signal_hook::flag; -use signal_hook::iterator::{Signals, SignalsInfo}; - -use crate::server; - -/// Install the required signal handlers for terminating signals. -pub fn install_signal_handlers() -> io::Result<(Arc, SignalsInfo)> { - let term = Arc::new(AtomicBool::new(false)); - - // For each terminating signal, we register both a shutdown handler and a handler that sets an - // atomic bool. With this, the process will get killed immediately once it receives a second - // termination signal (e.g. a double ctrl-c). - // https://docs.rs/signal-hook/0.3.15/signal_hook/#a-complex-signal-handling-with-a-background-thread - for sig in TERM_SIGNALS { - // When terminated by a second term signal, exit with exit code 1. - // This will do nothing the first time (because term_now is false). - flag::register_conditional_shutdown(*sig, 1, Arc::clone(&term))?; - // But this will "arm" the above for the second time, by setting it to true. - // The order of registering these is important, if you put this one first, it will - // first arm and then terminate ‒ all in the first round. - flag::register(*sig, Arc::clone(&term))?; - } - - let signals = TERM_SIGNALS; - - Ok((term, Signals::new(signals)?)) -} - -/// Loop that handles terminating signals as they come in. -pub fn handle_signals( - signals: &mut SignalsInfo, - counter: Arc>, -) -> io::Result<()> { - let mut force = false; - - // We only register terminating signals, so we don't need to differentiate between what kind of - // signal came in - for _ in signals { - // If term is already true, this is the second signal, meaning we kill the process - // immediately. - // This will currently not work, as the initial stop command will block the kill from - // happening. - if force { - let mut server = counter.lock().unwrap(); - return server.kill(); - } - // The stop command runs in a separate thread to avoid blocking the signal handling loop. - // After stopping the server, the thread terminates the process. - else { - let clone = Arc::clone(&counter); - - std::thread::spawn(move || { - let mut server = clone.lock().unwrap(); - let _ = server.stop(); - std::process::exit(0); - }); - } - - force = true; - } - - Ok(()) -} diff --git a/src/stdin.rs b/src/stdin.rs deleted file mode 100644 index f9a22e8..0000000 --- a/src/stdin.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::io; -use std::sync::{Arc, Mutex}; - -use crate::server; - -pub fn handle_stdin(counter: Arc>) { - let stdin = io::stdin(); - let input = &mut String::new(); - - loop { - input.clear(); - - if stdin.read_line(input).is_err() { - continue; - }; - - { - let mut server = counter.lock().unwrap(); - - if let Err(e) = server.send_command(input) { - println!("{}", e); - }; - } - - if input.trim() == "stop" { - std::process::exit(0); - } - } -}