feat: store server info in metadata file; change cli flags
							parent
							
								
									ef631fab1d
								
							
						
					
					
						commit
						53dc3783ca
					
				| 
						 | 
				
			
			@ -14,10 +14,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 | 
			
		|||
    * Chain length descibres how many incremental backups to create from the
 | 
			
		||||
      same full backup
 | 
			
		||||
    * "backups to keep" has been replaced by "chains to keep"
 | 
			
		||||
* Server type & version is now stored as metadata in the metadata file
 | 
			
		||||
 | 
			
		||||
### Changed
 | 
			
		||||
 | 
			
		||||
* Running the server now uses the `run` CLI subcommand
 | 
			
		||||
* `server_type` and `server_version` arguments are now optional flags
 | 
			
		||||
 | 
			
		||||
### Removed
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,7 @@ impl Delta {
 | 
			
		|||
 | 
			
		||||
    /// Update the current state so that its result becomes the merge of itself and the other
 | 
			
		||||
    /// state.
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    pub fn merge(&mut self, delta: &Self) {
 | 
			
		||||
        for (dir, added) in delta.added.iter() {
 | 
			
		||||
            // Files that were removed in the current state, but added in the new state, are no
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,18 +1,27 @@
 | 
			
		|||
use super::Backup;
 | 
			
		||||
use serde::de::DeserializeOwned;
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
use std::fs::File;
 | 
			
		||||
use std::io;
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
 | 
			
		||||
pub struct Manager {
 | 
			
		||||
pub struct Manager<T>
 | 
			
		||||
where
 | 
			
		||||
    T: Clone + Serialize + DeserializeOwned,
 | 
			
		||||
{
 | 
			
		||||
    backup_dir: PathBuf,
 | 
			
		||||
    config_dir: PathBuf,
 | 
			
		||||
    world_dir: PathBuf,
 | 
			
		||||
    default_metadata: T,
 | 
			
		||||
    chain_len: u64,
 | 
			
		||||
    chains_to_keep: u64,
 | 
			
		||||
    chains: Vec<Vec<Backup>>,
 | 
			
		||||
    chains: Vec<Vec<Backup<T>>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Manager {
 | 
			
		||||
impl<T> Manager<T>
 | 
			
		||||
where
 | 
			
		||||
    T: Clone + Serialize + DeserializeOwned,
 | 
			
		||||
{
 | 
			
		||||
    const METADATA_FILE: &str = "alex.json";
 | 
			
		||||
 | 
			
		||||
    /// Initialize a new instance of a `BackupManager`.
 | 
			
		||||
| 
						 | 
				
			
			@ -20,6 +29,7 @@ impl Manager {
 | 
			
		|||
        backup_dir: PathBuf,
 | 
			
		||||
        config_dir: PathBuf,
 | 
			
		||||
        world_dir: PathBuf,
 | 
			
		||||
        metadata: T,
 | 
			
		||||
        chain_len: u64,
 | 
			
		||||
        chains_to_keep: u64,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
| 
						 | 
				
			
			@ -27,6 +37,7 @@ impl Manager {
 | 
			
		|||
            backup_dir,
 | 
			
		||||
            config_dir,
 | 
			
		||||
            world_dir,
 | 
			
		||||
            default_metadata: metadata,
 | 
			
		||||
            chain_len,
 | 
			
		||||
            chains_to_keep,
 | 
			
		||||
            chains: Vec::new(),
 | 
			
		||||
| 
						 | 
				
			
			@ -40,32 +51,32 @@ impl Manager {
 | 
			
		|||
            (PathBuf::from("worlds"), self.world_dir.clone()),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        // I kinda hate this statement, please just let me combine let statements in if statements
 | 
			
		||||
        // already
 | 
			
		||||
        let backup = if let Some(current_chain) = self.chains.last() {
 | 
			
		||||
        // We start a new chain if the current chain is complete, or if there isn't a first chain
 | 
			
		||||
        // yet
 | 
			
		||||
        if let Some(current_chain) = self.chains.last() {
 | 
			
		||||
            let current_chain_len: u64 = current_chain.len().try_into().unwrap();
 | 
			
		||||
 | 
			
		||||
            if current_chain_len < self.chain_len {
 | 
			
		||||
                if let Some(previous_backup) = current_chain.last() {
 | 
			
		||||
                    let state = Backup::state(current_chain);
 | 
			
		||||
 | 
			
		||||
                    Backup::create_from(state, previous_backup.start_time, &self.backup_dir, dirs)?
 | 
			
		||||
                } else {
 | 
			
		||||
                    Backup::create(&self.backup_dir, dirs)?
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
            if current_chain_len >= self.chain_len {
 | 
			
		||||
                self.chains.push(Vec::new());
 | 
			
		||||
 | 
			
		||||
                Backup::create(&self.backup_dir, dirs)?
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            self.chains.push(Vec::new());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let current_chain = self.chains.last_mut().unwrap();
 | 
			
		||||
 | 
			
		||||
        let mut backup = if !current_chain.is_empty() {
 | 
			
		||||
            let previous_backup = current_chain.last().unwrap();
 | 
			
		||||
            let state = Backup::state(current_chain);
 | 
			
		||||
 | 
			
		||||
            Backup::create_from(state, previous_backup.start_time, &self.backup_dir, dirs)?
 | 
			
		||||
        } else {
 | 
			
		||||
            Backup::create(&self.backup_dir, dirs)?
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // The above statement always creates this element, so this unwrap is safe
 | 
			
		||||
        self.chains.last_mut().unwrap().push(backup);
 | 
			
		||||
        backup.set_metadata(self.default_metadata.clone());
 | 
			
		||||
 | 
			
		||||
        current_chain.push(backup);
 | 
			
		||||
 | 
			
		||||
        self.save()?;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -73,7 +84,7 @@ impl Manager {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    /// Delete all backups associated with outdated chains, and forget those chains.
 | 
			
		||||
    pub fn remove_old_backups(&mut self) -> std::io::Result<()> {
 | 
			
		||||
    pub fn remove_old_backups(&mut self) -> io::Result<()> {
 | 
			
		||||
        let chains_to_store: usize = self.chains_to_keep.try_into().unwrap();
 | 
			
		||||
 | 
			
		||||
        if chains_to_store < self.chains.len() {
 | 
			
		||||
| 
						 | 
				
			
			@ -91,15 +102,15 @@ impl Manager {
 | 
			
		|||
                    std::fs::remove_file(path)?;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.save()?;
 | 
			
		||||
            self.save()?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Write the in-memory state to disk.
 | 
			
		||||
    pub fn save(&self) -> std::io::Result<()> {
 | 
			
		||||
    pub fn save(&self) -> io::Result<()> {
 | 
			
		||||
        let json_file = File::create(self.backup_dir.join(Self::METADATA_FILE))?;
 | 
			
		||||
        serde_json::to_writer(json_file, &self.chains)?;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -107,7 +118,7 @@ impl Manager {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    /// Overwrite the in-memory state with the on-disk state.
 | 
			
		||||
    pub fn load(&mut self) -> std::io::Result<()> {
 | 
			
		||||
    pub fn load(&mut self) -> io::Result<()> {
 | 
			
		||||
        let json_file = match File::open(self.backup_dir.join(Self::METADATA_FILE)) {
 | 
			
		||||
            Ok(f) => f,
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -121,6 +132,7 @@ impl Manager {
 | 
			
		|||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        self.chains = serde_json::from_reader(json_file)?;
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,21 +22,37 @@ pub enum BackupType {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/// Represents a successful backup
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize)]
 | 
			
		||||
pub struct Backup {
 | 
			
		||||
#[derive(Serialize, Deserialize)]
 | 
			
		||||
pub struct Backup<T: Clone> {
 | 
			
		||||
    /// When the backup was started (also corresponds to the name)
 | 
			
		||||
    pub start_time: chrono::DateTime<Utc>,
 | 
			
		||||
    /// Type of the backup
 | 
			
		||||
    pub type_: BackupType,
 | 
			
		||||
    pub delta: Delta,
 | 
			
		||||
    /// Additional metadata that can be associated with a given backup
 | 
			
		||||
    pub metadata: Option<T>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Backup {
 | 
			
		||||
impl Backup<()> {
 | 
			
		||||
    /// Return the path to a backup file by properly formatting the data.
 | 
			
		||||
    pub fn path<P: AsRef<Path>>(backup_dir: P, start_time: chrono::DateTime<Utc>) -> PathBuf {
 | 
			
		||||
        let backup_dir = backup_dir.as_ref();
 | 
			
		||||
 | 
			
		||||
        let filename = format!("{}", start_time.format(Self::FILENAME_FORMAT));
 | 
			
		||||
        backup_dir.join(filename)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Clone> Backup<T> {
 | 
			
		||||
    const FILENAME_FORMAT: &str = "%Y-%m-%d_%H-%M-%S.tar.gz";
 | 
			
		||||
 | 
			
		||||
    pub fn set_metadata(&mut self, metadata: T) {
 | 
			
		||||
        self.metadata = Some(metadata);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Resolve the state of the list of backups by applying their deltas in-order to an initially
 | 
			
		||||
    /// empty state.
 | 
			
		||||
    pub fn state(backups: &Vec<Backup>) -> HashMap<PathBuf, HashSet<PathBuf>> {
 | 
			
		||||
    pub fn state(backups: &Vec<Self>) -> HashMap<PathBuf, HashSet<PathBuf>> {
 | 
			
		||||
        let mut state: HashMap<PathBuf, HashSet<PathBuf>> = HashMap::new();
 | 
			
		||||
 | 
			
		||||
        for backup in backups {
 | 
			
		||||
| 
						 | 
				
			
			@ -46,14 +62,6 @@ impl Backup {
 | 
			
		|||
        state
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Return the path to a backup file by properly formatting the data.
 | 
			
		||||
    pub fn path<P: AsRef<Path>>(backup_dir: P, start_time: chrono::DateTime<Utc>) -> PathBuf {
 | 
			
		||||
        let backup_dir = backup_dir.as_ref();
 | 
			
		||||
 | 
			
		||||
        let filename = format!("{}", start_time.format(Self::FILENAME_FORMAT));
 | 
			
		||||
        backup_dir.join(filename)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create a new Full backup, populated with the given directories.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Arguments
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +79,7 @@ impl Backup {
 | 
			
		|||
    ) -> io::Result<Self> {
 | 
			
		||||
        let start_time = chrono::offset::Utc::now();
 | 
			
		||||
 | 
			
		||||
        let path = Self::path(backup_dir, start_time);
 | 
			
		||||
        let path = Backup::path(backup_dir, start_time);
 | 
			
		||||
        let tar_gz = File::create(path)?;
 | 
			
		||||
        let enc = GzEncoder::new(tar_gz, Compression::default());
 | 
			
		||||
        let mut ar = tar::Builder::new(enc);
 | 
			
		||||
| 
						 | 
				
			
			@ -96,6 +104,7 @@ impl Backup {
 | 
			
		|||
            type_: BackupType::Full,
 | 
			
		||||
            start_time,
 | 
			
		||||
            delta,
 | 
			
		||||
            metadata: None,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -108,7 +117,7 @@ impl Backup {
 | 
			
		|||
    ) -> io::Result<Self> {
 | 
			
		||||
        let start_time = chrono::offset::Utc::now();
 | 
			
		||||
 | 
			
		||||
        let path = Self::path(backup_dir, start_time);
 | 
			
		||||
        let path = Backup::path(backup_dir, start_time);
 | 
			
		||||
        let tar_gz = File::create(path)?;
 | 
			
		||||
        let enc = GzEncoder::new(tar_gz, Compression::default());
 | 
			
		||||
        let mut ar = tar::Builder::new(enc);
 | 
			
		||||
| 
						 | 
				
			
			@ -145,6 +154,7 @@ impl Backup {
 | 
			
		|||
            type_: BackupType::Incremental,
 | 
			
		||||
            start_time,
 | 
			
		||||
            delta,
 | 
			
		||||
            metadata: None,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										20
									
								
								src/cli.rs
								
								
								
								
							
							
						
						
									
										20
									
								
								src/cli.rs
								
								
								
								
							| 
						 | 
				
			
			@ -53,6 +53,12 @@ pub struct Cli {
 | 
			
		|||
        global = true
 | 
			
		||||
    )]
 | 
			
		||||
    pub chains: u64,
 | 
			
		||||
    /// Type of server
 | 
			
		||||
    #[arg(long, default_value = "unknown", env = "ALEX_SERVER")]
 | 
			
		||||
    pub server: ServerType,
 | 
			
		||||
    /// Version string for the server, e.g. 1.19.4-545
 | 
			
		||||
    #[arg(long, default_value = "", env = "ALEX_SERVER_VERSION")]
 | 
			
		||||
    pub server_version: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Subcommand)]
 | 
			
		||||
| 
						 | 
				
			
			@ -66,12 +72,6 @@ pub enum Commands {
 | 
			
		|||
 | 
			
		||||
#[derive(Args)]
 | 
			
		||||
pub struct RunArgs {
 | 
			
		||||
    /// Type of server
 | 
			
		||||
    pub type_: ServerType,
 | 
			
		||||
    /// Version string for the server, e.g. 1.19.4-545
 | 
			
		||||
    #[arg(env = "ALEX_SERVER_VERSION")]
 | 
			
		||||
    pub server_version: String,
 | 
			
		||||
 | 
			
		||||
    /// Server jar to execute
 | 
			
		||||
    #[arg(
 | 
			
		||||
        long,
 | 
			
		||||
| 
						 | 
				
			
			@ -103,10 +103,4 @@ pub struct RunArgs {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Args)]
 | 
			
		||||
pub struct BackupArgs {
 | 
			
		||||
    /// Type of server
 | 
			
		||||
    pub type_: ServerType,
 | 
			
		||||
    /// Version string for the server, e.g. 1.19.4-545
 | 
			
		||||
    #[arg(env = "ALEX_SERVER_VERSION")]
 | 
			
		||||
    pub server_version: String,
 | 
			
		||||
}
 | 
			
		||||
pub struct BackupArgs {}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,7 @@ fn backups_thread(counter: Arc<Mutex<server::ServerProcess>>, frequency: u64) {
 | 
			
		|||
fn command_run(cli: &Cli, args: &RunArgs) -> io::Result<()> {
 | 
			
		||||
    let (_, mut signals) = signals::install_signal_handlers()?;
 | 
			
		||||
 | 
			
		||||
    let mut cmd = server::ServerCommand::new(args.type_, &args.server_version)
 | 
			
		||||
    let mut cmd = server::ServerCommand::new(cli.server, &cli.server_version)
 | 
			
		||||
        .java(&args.java)
 | 
			
		||||
        .jar(args.jar.clone())
 | 
			
		||||
        .config(cli.config.clone())
 | 
			
		||||
| 
						 | 
				
			
			@ -60,10 +60,16 @@ fn command_run(cli: &Cli, args: &RunArgs) -> io::Result<()> {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
fn commands_backup(cli: &Cli, _args: &BackupArgs) -> io::Result<()> {
 | 
			
		||||
    let metadata = server::Metadata {
 | 
			
		||||
        server_type: cli.server,
 | 
			
		||||
        server_version: cli.server_version.clone(),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let mut manager = backup::Manager::new(
 | 
			
		||||
        cli.backup.clone(),
 | 
			
		||||
        cli.config.clone(),
 | 
			
		||||
        cli.world.clone(),
 | 
			
		||||
        metadata,
 | 
			
		||||
        cli.chain_len,
 | 
			
		||||
        cli.chains,
 | 
			
		||||
    );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,14 +1,16 @@
 | 
			
		|||
use crate::backup::Manager as BackupManager;
 | 
			
		||||
use crate::server::ServerProcess;
 | 
			
		||||
use clap::ValueEnum;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use std::fmt;
 | 
			
		||||
use std::fs::File;
 | 
			
		||||
use std::io::Write;
 | 
			
		||||
use std::path::{Path, PathBuf};
 | 
			
		||||
use std::process::{Command, Stdio};
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
 | 
			
		||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)]
 | 
			
		||||
pub enum ServerType {
 | 
			
		||||
    Unknown,
 | 
			
		||||
    Paper,
 | 
			
		||||
    Forge,
 | 
			
		||||
    Vanilla,
 | 
			
		||||
| 
						 | 
				
			
			@ -17,6 +19,7 @@ pub enum ServerType {
 | 
			
		|||
impl fmt::Display for ServerType {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        let s = match self {
 | 
			
		||||
            ServerType::Unknown => "Unknown",
 | 
			
		||||
            ServerType::Paper => "PaperMC",
 | 
			
		||||
            ServerType::Forge => "Forge",
 | 
			
		||||
            ServerType::Vanilla => "Vanilla",
 | 
			
		||||
| 
						 | 
				
			
			@ -26,6 +29,12 @@ impl fmt::Display for ServerType {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct Metadata {
 | 
			
		||||
    pub server_type: ServerType,
 | 
			
		||||
    pub server_version: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct ServerCommand {
 | 
			
		||||
    type_: ServerType,
 | 
			
		||||
    version: String,
 | 
			
		||||
| 
						 | 
				
			
			@ -187,10 +196,15 @@ impl ServerCommand {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn spawn(&mut self) -> std::io::Result<ServerProcess> {
 | 
			
		||||
        let metadata = Metadata {
 | 
			
		||||
            server_type: self.type_,
 | 
			
		||||
            server_version: self.version.clone(),
 | 
			
		||||
        };
 | 
			
		||||
        let mut manager = BackupManager::new(
 | 
			
		||||
            self.backup_dir.clone(),
 | 
			
		||||
            self.config_dir.clone(),
 | 
			
		||||
            self.world_dir.clone(),
 | 
			
		||||
            metadata,
 | 
			
		||||
            self.chain_len,
 | 
			
		||||
            self.chains_to_keep,
 | 
			
		||||
        );
 | 
			
		||||
| 
						 | 
				
			
			@ -200,12 +214,7 @@ impl ServerCommand {
 | 
			
		|||
        self.accept_eula()?;
 | 
			
		||||
        let child = cmd.spawn()?;
 | 
			
		||||
 | 
			
		||||
        Ok(ServerProcess::new(
 | 
			
		||||
            self.type_,
 | 
			
		||||
            self.version.clone(),
 | 
			
		||||
            manager,
 | 
			
		||||
            child,
 | 
			
		||||
        ))
 | 
			
		||||
        Ok(ServerProcess::new(manager, child))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
mod command;
 | 
			
		||||
mod process;
 | 
			
		||||
 | 
			
		||||
pub use command::{ServerCommand, ServerType};
 | 
			
		||||
pub use command::{Metadata, ServerCommand, ServerType};
 | 
			
		||||
pub use process::ServerProcess;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,25 +1,16 @@
 | 
			
		|||
use crate::backup::Manager as BackupManager;
 | 
			
		||||
use crate::server::ServerType;
 | 
			
		||||
use crate::server::Metadata;
 | 
			
		||||
use std::io::Write;
 | 
			
		||||
use std::process::Child;
 | 
			
		||||
 | 
			
		||||
pub struct ServerProcess {
 | 
			
		||||
    type_: ServerType,
 | 
			
		||||
    version: String,
 | 
			
		||||
    child: Child,
 | 
			
		||||
    backups: BackupManager,
 | 
			
		||||
    backups: BackupManager<Metadata>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ServerProcess {
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        type_: ServerType,
 | 
			
		||||
        version: String,
 | 
			
		||||
        manager: BackupManager,
 | 
			
		||||
        child: Child,
 | 
			
		||||
    ) -> ServerProcess {
 | 
			
		||||
    pub fn new(manager: BackupManager<Metadata>, child: Child) -> ServerProcess {
 | 
			
		||||
        ServerProcess {
 | 
			
		||||
            type_,
 | 
			
		||||
            version,
 | 
			
		||||
            child,
 | 
			
		||||
            backups: manager,
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue