use std::error::Error;
use std::net::TcpListener;
use std::path::PathBuf;
use std::thread;
use clap;
use num_cpus;
use output::Output;
pub use server::handler::handle_client;
use ::zbackup::repository::*;
use ::misc::*;
pub fn run_server (
output: & Output,
arguments: & ServerArguments,
) -> Result <bool, String> {
let repository =
string_result_with_prefix (
|| format! (
"Error opening repository: "),
Repository::open (
& output,
arguments.repository_config.clone (),
& arguments.repository_path,
arguments.password_file_path.as_ref (),
),
) ?;
output.message (
"RZBackup startup complete");
string_result_with_prefix (
|| format! (
"RZBackup server encountered error: "),
run_server_listener (
repository.clone (),
& arguments.listen_address,
),
) ?;
repository.close (
output);
output.message (
"RZBackup server terminating normally");
Ok (true)
}
pub fn run_server_listener (
repository: Repository,
bind_address: & str,
) -> Result <(), String> {
let listener =
io_result (
TcpListener::bind (
bind_address),
) ?;
for stream
in listener.incoming () {
match stream {
Ok (stream) => {
let repository_copy =
repository.clone ();
thread::spawn (
move || {
handle_client (
& repository_copy,
stream)
}
);
},
Err (error) => {
println! (
"Connection failed: {}",
error.description ());
},
}
};
Ok (())
}
fn repository_config_parse (
clap_matches: & clap::ArgMatches,
) -> RepositoryConfig {
RepositoryConfig {
max_uncompressed_memory_cache_entries:
args::u64_required (
clap_matches,
"max-uncompressed-memory-cache-entries",
) as usize,
max_compressed_memory_cache_entries:
args::u64_required (
clap_matches,
"max-compressed-memory-cache-entries",
) as usize,
max_compressed_filesystem_cache_entries:
args::u64_required (
clap_matches,
"max-compressed-filesystem-cache-entries",
) as usize,
max_threads:
args::u64_required (
clap_matches,
"max-threads",
) as usize,
filesystem_cache_path:
args::path_required (
clap_matches,
"filesystem-cache-path",
).to_string_lossy ().to_string (),
work_jobs_total: 0, work_jobs_batch: 0,
}
}
command! (
name = server,
export = server_command,
arguments = ServerArguments {
repository_path: PathBuf,
password_file_path: Option <PathBuf>,
repository_config: RepositoryConfig,
listen_address: String,
},
clap_subcommand = {
lazy_static! {
static ref DEFAULT_MAX_UNCOMPRESSED_MEMORY_CACHE_ENTRIES_STRING: String =
::MAX_UNCOMPRESSED_MEMORY_CACHE_ENTRIES.to_string ();
static ref DEFAULT_MAX_COMPRESSED_MEMORY_CACHE_ENTRIES_STRING: String =
::MAX_COMPRESSED_MEMORY_CACHE_ENTRIES.to_string ();
static ref DEFAULT_MAX_COMPRESSED_FILESYSTEM_CACHE_ENTRIES_STRING: String =
::MAX_COMPRESSED_FILESYSTEM_CACHE_ENTRIES.to_string ();
static ref DEFAULT_MAX_THREADS_STRING: String =
num_cpus::get ().to_string ();
static ref DEFAULT_FILESYSTEM_CACHE_PATH_STRING: String =
::FILESYSTEM_CACHE_PATH.to_string ();
}
clap::SubCommand::with_name ("server")
.about ("Server component")
.arg (
clap::Arg::with_name ("repository")
.long ("repository")
.alias ("repository-path")
.value_name ("REPOSITORY")
.required (true)
.help ("Path to the repository, used to obtain encryption key")
)
.arg (
clap::Arg::with_name ("password-file")
.long ("password-file")
.alias ("password-file-path")
.value_name ("PASSWORD-FILE")
.required (false)
.help ("Path to the password file")
)
.arg (
clap::Arg::with_name ("listen-address")
.long ("listen-address")
.value_name ("ADDRESS:PORT")
.default_value ("localhost:4152")
.help ("Address to listen on, in host:port or ip:port format.")
)
.arg (
clap::Arg::with_name ("max-uncompressed-memory-cache-entries")
.long ("max-uncompressed-memory-cache-entries")
.value_name ("ENTRIES")
.default_value (
& DEFAULT_MAX_UNCOMPRESSED_MEMORY_CACHE_ENTRIES_STRING)
.help ("Size of very high speed, very high cost, in-memory \
cache of uncompressed cache entries.")
)
.arg (
clap::Arg::with_name ("max-compressed-memory-cache-entries")
.long ("max-compressed-memory-cache-entries")
.value_name ("ENTRIES")
.default_value (
& DEFAULT_MAX_COMPRESSED_MEMORY_CACHE_ENTRIES_STRING)
.help ("Size of high speed, high cost, in memory cache of \
compressed cache entries.")
)
.arg (
clap::Arg::with_name ("max-compressed-filesystem-cache-entries")
.long ("max-compressed-filesystem-cache-entries")
.value_name ("ENTRIES")
.default_value (
& DEFAULT_MAX_COMPRESSED_FILESYSTEM_CACHE_ENTRIES_STRING)
.help ("Size of medium speed, low cost, on-disk cache of \
compressed cache entries.")
)
.arg (
clap::Arg::with_name ("max-threads")
.long ("max-threads")
.value_name ("THREADS")
.default_value (
& DEFAULT_MAX_THREADS_STRING)
.help ("Number of worker threads to execute. The default value \
is determined by the number of CPU threads reported by the \
operating system.")
)
.arg (
clap::Arg::with_name ("filesystem-cache-path")
.long ("filesystem-cache-path")
.value_name ("PATH")
.default_value (
& DEFAULT_FILESYSTEM_CACHE_PATH_STRING)
.help ("Location of the filesystem cache. This will be used to \
store chunks which have been decompressed, typically from \
slow but efficient LZMA compressed bundles, and \
recompressed using LZO, then saved to disk and managed \
individually using an LRU cache algorithm.")
)
.arg (
clap::Arg::with_name ("work-jobs-total")
.long ("work-jobs-total")
.value_name ("JOBS")
.default_value ("0")
.hidden (true)
.help ("Deprecated and ignored")
)
.arg (
clap::Arg::with_name ("work-jobs-batch")
.long ("work-jobs-batch")
.value_name ("JOBS")
.default_value ("0")
.hidden (true)
.help ("Deprecated and ignored")
)
},
clap_arguments_parse = |clap_matches| {
ServerArguments {
repository_path:
args::path_required (
clap_matches,
"repository"),
password_file_path:
args::path_optional (
clap_matches,
"password-file"),
repository_config:
repository_config_parse (
clap_matches),
listen_address:
args::string_required (
clap_matches,
"listen-address"),
}
},
action = |output, arguments| {
run_server (output, arguments)
},
);