use std::{
env,
error::Error,
fs::{File, OpenOptions},
io::{Read, Write},
os::unix::io::AsRawFd,
str::FromStr,
};
use clap::{App, Arg};
use env_logger::Builder;
use log::LevelFilter;
use nix::{
fcntl::{flock, FlockArg},
unistd::getpid,
};
use libstratis::stratis::{run, StratisError, StratisResult, VERSION};
const STRATISD_PID_PATH: &str = "/run/stratisd.pid";
const STRATISD_MIN_PID_PATH: &str = "/run/stratisd-min.pid";
fn parse_args() -> App<'static, 'static> {
App::new("stratisd-min")
.version(VERSION)
.arg(
Arg::with_name("log_level")
.empty_values(false)
.long("--log-level")
.possible_values(&["trace", "debug", "info", "warn", "error"])
.help("Sets level for generation of log messages."),
)
.arg(
Arg::with_name("sim")
.long("--sim")
.takes_value(false)
.help("Enables sim engine."),
)
}
fn get_long_help(app: &mut App) -> Result<String, Box<dyn Error>> {
let mut help = Vec::new();
app.write_long_help(&mut help)?;
Ok(String::from_utf8(help)?)
}
fn trylock_pid_file() -> StratisResult<File> {
let mut f = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(STRATISD_MIN_PID_PATH)
.map_err(|err| {
StratisError::Error(format!(
"Failed to create or open the stratisd-min PID file at {}: {}",
STRATISD_MIN_PID_PATH, err
))
})?;
let stratisd_min_file = match flock(f.as_raw_fd(), FlockArg::LockExclusiveNonblock) {
Ok(_) => {
f.write_all(getpid().to_string().as_bytes())?;
f
}
Err(_) => {
let mut buf = String::new();
if f.read_to_string(&mut buf).is_err() {
buf = "<unreadable>".to_string();
}
return Err(StratisError::Error(format!(
"Daemon already running with supposed pid: {}",
buf
)));
}
};
let mut f = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(STRATISD_PID_PATH)
.map_err(|err| {
StratisError::Error(format!(
"Failed to create or open the stratisd PID file at {}: {}",
STRATISD_PID_PATH, err
))
})?;
match flock(f.as_raw_fd(), FlockArg::LockExclusiveNonblock) {
Ok(_) => drop(f),
Err(_) => {
let mut buf = String::new();
if f.read_to_string(&mut buf).is_err() {
buf = "<unreadable>".to_string();
}
return Err(StratisError::Error(format!(
"stratisd is already running with supposed pid: {}",
buf
)));
}
};
Ok(stratisd_min_file)
}
fn main() -> Result<(), Box<dyn Error>> {
let mut app = parse_args();
let help = get_long_help(&mut app)?;
let args = app.get_matches();
let _stratisd_min_file = trylock_pid_file()?;
let mut builder = Builder::new();
if let Some(log_level) = args.value_of("log_level") {
builder.filter(
Some("stratisd"),
LevelFilter::from_str(log_level)
.expect("argument parser only accepts valid log levels"),
);
builder.filter(
Some("libstratis"),
LevelFilter::from_str(log_level)
.expect("argument parser only accepts valid log levels"),
);
} else if let Ok(s) = env::var("RUST_LOG") {
builder.parse_filters(&s);
}
builder.init();
if args.is_present("-h") {
println!("{}", help);
Ok(())
} else {
run(args.is_present("sim"))?;
Ok(())
}
}