use std::path::PathBuf;
use anyhow::Context;
use anyhow::Result;
use sequoia_ipc as ipc;
use sequoia_keystore as keystore;
use clap::Parser;
use clap::ValueEnum;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
enum Ephemeral {
True,
False,
}
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
#[clap(
long,
env = "SEQUOIA_HOME",
help = "Set the home directory",
long_help = format!("\
Set the home directory.
Sequoia's default home directory is `{}`. When using the default \
location, files are placed according to the local standard, \
e.g., the XDG Base Directory Specification. When an alternate \
location is specified, the user data, configuration files, and \
cache data are placed under a single, unified directory. This is \
a lightweight way to partially isolate programs.",
sequoia_directories::Home::default_location()
.map(|p| {
let p = p.display().to_string();
if let Some(home) = dirs::home_dir() {
let home = home.display().to_string();
if let Some(rest) = p.strip_prefix(&home) {
return format!("$HOME{}", rest);
}
}
p
})
.unwrap_or("<unknown>".to_string())),
)]
pub sequoia_home: Option<PathBuf>,
#[clap(
long,
help = "Set the keystore's home directory",
long_help = format!("\
Set the component's home directory.
Sequoia's default home directory is `{}`. The keystore's \
default home directory is `{}`. If this option is set, this \
overrides `--sequoia-home`.",
sequoia_directories::Home::default_location()
.map(|p| {
let p = p.display().to_string();
if let Some(home) = dirs::home_dir() {
let home = home.display().to_string();
if let Some(rest) = p.strip_prefix(&home) {
return format!("$HOME{}", rest);
}
}
p
})
.unwrap_or("<unknown>".to_string()),
sequoia_directories::Home::default()
.map(|home| {
home.data_dir(sequoia_directories::Component::Keystore)
})
.map(|p| {
let p = p.display().to_string();
if let Some(home) = dirs::home_dir() {
let home = home.display().to_string();
if let Some(rest) = p.strip_prefix(&home) {
return format!("$HOME{}", rest);
}
}
p
})
.unwrap_or("<unknown>".to_string())),
)]
pub home: Option<PathBuf>,
#[clap(
hide = true,
long,
value_enum,
help = "Whether to create an ephemeral context",
long_help = "\
Whether to create an ephemeral context.
An ephemeral context is destroyed when the program exists.
",
)]
pub ephemeral: Option<Ephemeral>,
#[clap(
hide = true,
long,
help = "Set the directory containing the backend servers",
)]
pub lib: Option<PathBuf>,
#[clap(
long,
help = "The socket is passed on the given file descriptor",
long_help = "\
The socket is passed on the given file descriptor.
This option is normally only used by programs starting the server.",
)]
pub socket: Option<usize>,
}
fn main() -> Result<()> {
let args = Args::parse();
let keystore_home = if let Some(keystore_home) = args.home {
keystore_home
} else {
let home = sequoia_directories::Home::new(args.sequoia_home)?;
home.data_dir(sequoia_directories::Component::Keystore)
};
std::fs::create_dir_all(&keystore_home)
.context(format!("Creating {}", keystore_home.display()))?;
let mut config = keystore::Context::configure()
.home(&keystore_home);
if args.ephemeral == Some(Ephemeral::True) {
config.set_ephemeral();
}
if let Some(path) = args.lib {
config.set_lib(path);
}
let context = config.build()?;
let mut desc = keystore::descriptor(&context);
if let Some(fd) = args.socket {
if fd != 0 {
eprintln!("Currently the socket can only be passed on fd 0");
std::process::exit(1);
}
let mut server = ipc::Server::new(desc)?;
server.serve()?;
} else {
let join_handle = desc.bootstrap()?;
if let Some(join_handle) = join_handle {
match join_handle.join() {
Err(err) => panic!("The server thread panicked: {:?}", err),
Ok(Err(err)) => return Err(err),
Ok(Ok(())) => (),
}
}
}
Ok(())
}