use crate::logger::Logger;
use crate::utils::py_stamp;
#[cfg(not(target_os = "windows"))]
use daemonize::Daemonize;
#[cfg(not(target_os = "windows"))]
use std::fs;
#[cfg(not(target_os = "windows"))]
use std::fs::File;
#[cfg(not(target_os = "windows"))]
use std::path::Path;
const PORT: &'static str = "8587";
#[cfg(not(target_os = "linux"))]
const RESIZE: &'static str = "4";
#[cfg(target_os = "linux")]
const RESIZE: &'static str = "1";
const VERBOSE_ALL: &'static str = "3";
const VERBOSE_INFO: &'static str = "2";
const VERBOSE_ERRORS: &'static str = "1";
const VERBOSE_SILENT: &'static str = "0";
pub struct Params {
pub started: f64,
pub port: u16,
pub resize: usize,
pub(crate) threads: usize,
#[cfg(not(target_os = "windows"))]
stdout: String,
#[cfg(not(target_os = "windows"))]
stderr: String,
#[cfg(not(target_os = "windows"))]
pid: String,
#[cfg(not(target_os = "windows"))]
user: String,
#[cfg(not(target_os = "windows"))]
group: String,
#[cfg(not(target_os = "windows"))]
workdir: String,
pub(crate) log_level: log::LevelFilter,
pub(crate) daemon: bool,
}
impl Params {
fn remove_pid_file(&self) {
#[cfg(not(target_os = "windows"))]
let _ = fs::remove_file(&self.pid);
}
pub(crate) fn from_command_line() -> Params {
#[cfg(not(target_os = "windows"))]
let user = whoami::username();
let about = about();
let threads = num_cpus::get().to_string();
let matches = clap::App::new(env!("CARGO_PKG_NAME"))
.about(<std::string::String as AsRef<str>>::as_ref(&about))
.arg(
clap::Arg::with_name("port")
.value_name("PORT")
.help("TCP/IP port to listen for client connections")
.default_value(PORT)
.takes_value(true),
)
.arg(
clap::Arg::with_name("resize")
.short("r")
.long("resize")
.value_name("PIXELS")
.help("Resize and interpolate image by PIXELS before sending to client")
.default_value(RESIZE)
.takes_value(true),
)
.arg(
clap::Arg::with_name("verbose")
.short("v")
.long("verbose")
.value_name("VERBOSITY LEVEL")
.help("Verbosity level for messages: 0 - off; 1 - errors; 2 - info; 3 - all")
.default_value(VERBOSE_INFO)
.takes_value(true),
)
.arg(
clap::Arg::with_name("threads")
.short("t")
.long("threads")
.value_name("THREAD NUMBER")
.help("Number of threads which are used for integration")
.default_value(&threads)
.takes_value(true),
);
#[cfg(not(target_os = "windows"))]
let matches = matches
.arg(
clap::Arg::with_name("stdout")
.short("o")
.long("out")
.value_name("LOGFILE")
.help("Log file")
.takes_value(true),
)
.arg(
clap::Arg::with_name("stderr")
.short("e")
.long("err")
.value_name("LOGFILE")
.help("Log file for errors")
.takes_value(true),
)
.arg(
clap::Arg::with_name("pid")
.short("i")
.long("pid")
.value_name("PIDFILE")
.help("pidfile path")
.takes_value(true),
)
.arg(
clap::Arg::with_name("user")
.short("u")
.long("user")
.value_name("USER")
.help("A username to run daemon")
.default_value(&user)
.takes_value(true),
)
.arg(
clap::Arg::with_name("group")
.short("g")
.long("group")
.value_name("GROUP")
.help("A group to run daemon")
.default_value(&user)
.takes_value(true),
)
.arg(
clap::Arg::with_name("workdir")
.short("w")
.long("workdir")
.value_name("WORKING DIRECTORY")
.help("Working directory for the daemon")
.takes_value(true),
)
.arg(
clap::Arg::with_name("daemon")
.short("d")
.long("daemon")
.help("Run process in the background"),
);
let matches = matches.get_matches();
let p = Params {
port: value_t!(matches.value_of("port"), u16).unwrap_or_else(|e| e.exit()),
resize: value_t!(matches.value_of("resize"), usize).unwrap_or_else(|e| e.exit()),
threads: value_t!(matches.value_of("threads"), usize).unwrap_or_else(|e| e.exit()),
#[cfg(not(target_os = "windows"))]
stdout: matches.value_of("stdout").or(Some("")).unwrap().to_string(),
#[cfg(not(target_os = "windows"))]
stderr: matches.value_of("stderr").or(Some("")).unwrap().to_string(),
#[cfg(not(target_os = "windows"))]
pid: matches.value_of("pid").or(Some("")).unwrap().to_string(),
#[cfg(not(target_os = "windows"))]
user: matches
.value_of("user")
.or(Some(&user))
.unwrap()
.to_string(),
#[cfg(not(target_os = "windows"))]
group: matches.value_of("group").or(Some("")).unwrap().to_string(),
#[cfg(not(target_os = "windows"))]
workdir: matches
.value_of("workdir")
.or(Some(""))
.unwrap()
.to_string(),
started: py_stamp(),
log_level: match matches
.value_of("verbose")
.unwrap_or_else(|| std::process::exit(1))
{
VERBOSE_ERRORS => log::LevelFilter::Error,
VERBOSE_ALL => log::LevelFilter::Debug,
VERBOSE_SILENT => log::LevelFilter::Off,
VERBOSE_INFO => log::LevelFilter::Info,
_ => log::LevelFilter::Debug,
},
#[cfg(not(target_os = "windows"))]
daemon: matches.is_present("daemon"),
#[cfg(target_os = "windows")]
daemon: false,
};
if let Err(e) = Logger::init(&p) {
die!("Could not set logger: {}", e);
}
p.daemonize();
println!("{} {}", env!("CARGO_PKG_NAME"), about);
p
}
#[cfg(target_os = "windows")]
fn daemonize(&self) {}
#[cfg(not(target_os = "windows"))]
fn daemonize(&self) {
if !self.daemon {
return;
}
let mut daemon = Daemonize::new();
if let Some(out) = self.stdout.create() {
daemon = daemon.stdout(out);
}
if self.stderr.is_empty() {
if !self.stdout.is_empty() {
if let Some(out) = self.stdout.create() {
daemon = daemon.stderr(out);
}
}
} else {
if let Some(err) = self.stderr.create() {
daemon = daemon.stderr(err);
}
}
if !self.pid.is_empty() {
daemon = daemon.pid_file(&self.pid);
}
if !self.workdir.is_empty() {
daemon = daemon.working_directory(&self.workdir);
}
if !self.user.is_empty() {
daemon = daemon.user(self.user.as_ref());
}
if !self.group.is_empty() {
daemon = daemon.group(self.group.as_ref());
}
match daemon.start() {
Ok(_) => info!("The main process has forked successfully"),
Err(e) => die!("Could not daemonize: {}", e),
}
}
pub(crate) fn address(&self) -> String {
format!("0.0.0.0:{}", self.port)
}
}
#[cfg(not(target_os = "windows"))]
trait FileCreator: AsRef<Path> {
fn create(&self) -> Option<fs::File>;
}
#[cfg(not(target_os = "windows"))]
impl FileCreator for str {
fn create(&self) -> Option<File> {
if !self.is_empty() {
match fs::OpenOptions::new().append(true).create(true).open(self) {
Ok(out) => Some(out),
Err(e) => {
error!("Failed to open file {}: {}", self, e);
None
}
}
} else {
None
}
}
}
impl Drop for Params {
fn drop(&mut self) {
self.remove_pid_file();
}
}
fn about() -> String {
format!(
"is {}, version {} built on {} {}\n\
(c) {} 2014-{}, SNBL@ESRF, inspired by Giuseppe Portale\n\
If this program proves to be useful, please cite this paper:\n\
http://dx.doi.org/10.1107/S1600577516002411\n\
Official web page: {}\n\
Git repository: {}\n\
Git hash: {}\n\
Git date: {} {}",
env!("CARGO_PKG_DESCRIPTION"),
env!("CARGO_PKG_VERSION"),
env!("VERGEN_BUILD_DATE"),
env!("VERGEN_BUILD_TIME"),
env!("CARGO_PKG_AUTHORS"),
&env!("VERGEN_BUILD_DATE")[..4],
env!("CARGO_PKG_HOMEPAGE"),
env!("CARGO_PKG_REPOSITORY"),
env!("VERGEN_GIT_SHA"),
env!("VERGEN_GIT_COMMIT_DATE"),
env!("VERGEN_GIT_COMMIT_TIME"),
)
}