178 lines
6.8 KiB
Rust
178 lines
6.8 KiB
Rust
use core::{net, str};
|
|
use std::{
|
|
fs,
|
|
io::{self, BufRead, BufReader, Write, stdout},
|
|
os::unix::net::{UnixListener, UnixStream},
|
|
process,
|
|
sync::Arc,
|
|
thread::{self, JoinHandle},
|
|
};
|
|
|
|
use anyhow::Context;
|
|
use parking_lot::RwLockWriteGuard;
|
|
|
|
use crate::config;
|
|
|
|
// TODO: make more anyhow
|
|
pub fn start_client(config: config::Schema) -> anyhow::Result<()> {
|
|
let mut stream = UnixStream::connect(config.ipc.context("no ipc socket specified")?)
|
|
.context("failed to connect to unix socket")?;
|
|
|
|
let stream2 = stream.try_clone().context("failed to clone stream")?;
|
|
let mut reader = BufReader::new(stream2);
|
|
|
|
let stdin = io::stdin();
|
|
|
|
print!("> ");
|
|
_ = stdout().flush();
|
|
for line in stdin.lines() {
|
|
let Ok(line) = line else {
|
|
break;
|
|
};
|
|
_ = stream.write_all(line.as_bytes());
|
|
_ = stream.write_all(b"\n");
|
|
|
|
for line in (&mut reader).lines() {
|
|
let Ok(line) = line else {
|
|
return Ok(());
|
|
};
|
|
if line == "EOF" {
|
|
break;
|
|
}
|
|
println!("{line}");
|
|
}
|
|
|
|
print!("> ");
|
|
_ = stdout().flush();
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn handle_daemon_client(mut stream: UnixStream, config: &Arc<config::Schema>) {
|
|
let Ok(stream2) = stream.try_clone() else {
|
|
return;
|
|
};
|
|
for line in BufReader::new(stream2).lines() {
|
|
let Ok(line) = line else {
|
|
return;
|
|
};
|
|
let args = shlex::Shlex::new(line.trim()).collect::<Vec<_>>();
|
|
|
|
// i hate it too
|
|
if let [arg0, args @ ..] = &args[..] {
|
|
match arg0.as_str() {
|
|
"help" => {
|
|
_ = writeln!(
|
|
stream,
|
|
"avaliable commands:\n help\n configcount\n confdump\n hosts <list|create|delete> ..."
|
|
);
|
|
}
|
|
"configcount" => {
|
|
_ = writeln!(
|
|
stream,
|
|
"weak {}, strong {}",
|
|
Arc::weak_count(config),
|
|
Arc::strong_count(config)
|
|
);
|
|
}
|
|
"confdump" => _ = writeln!(stream, "{:#?}", config.as_ref()),
|
|
"hosts" => {
|
|
if let [arg0, args @ ..] = args {
|
|
match arg0.as_str() {
|
|
"list" => {
|
|
_ = writeln!(stream, "aquiring read lock");
|
|
let rlock = config.hosts.read();
|
|
_ = writeln!(stream, "{rlock:#?}");
|
|
}
|
|
"create" => {
|
|
if let [name, value] = args {
|
|
if let Ok([value, id, csrf]) =
|
|
<_ as TryInto<[&str; 3]>>::try_into(
|
|
value.split(',').take(3).collect::<Vec<_>>(),
|
|
)
|
|
{
|
|
match value.parse::<net::SocketAddr>() {
|
|
Ok(addr) => {
|
|
_ = writeln!(stream, "aquiring write lock");
|
|
let mut wlock = config.hosts.write();
|
|
wlock.insert(
|
|
name.to_string(),
|
|
(addr, id.to_string(), csrf.to_string()),
|
|
);
|
|
let rlock = RwLockWriteGuard::downgrade(wlock);
|
|
_ = writeln!(stream, "{rlock:#?}");
|
|
}
|
|
Err(err) => {
|
|
_ = writeln!(
|
|
stream,
|
|
"err: parsing value as socket addres {err:?}"
|
|
);
|
|
}
|
|
}
|
|
} else {
|
|
_ = writeln!(
|
|
stream,
|
|
"err: value wasn't 3 comma separated values"
|
|
);
|
|
}
|
|
} else {
|
|
_ = writeln!(
|
|
stream,
|
|
"invalid arg count, expected name and value"
|
|
);
|
|
}
|
|
}
|
|
"delete" => {
|
|
if let [name] = args {
|
|
_ = writeln!(stream, "aquiring write lock");
|
|
let mut wlock = config.hosts.write();
|
|
wlock.remove(name);
|
|
let lock = RwLockWriteGuard::downgrade(wlock);
|
|
_ = writeln!(stream, "{lock:#?}");
|
|
} else {
|
|
_ = writeln!(stream, "invalid arg count, expected a name");
|
|
}
|
|
}
|
|
_ => _ = writeln!(stream, "err: invalid argument {arg0:?}"),
|
|
}
|
|
} else {
|
|
_ = writeln!(stream, "err: not enough arguments");
|
|
}
|
|
}
|
|
_ => {
|
|
_ = writeln!(stream, "command {arg0:?} doesn't exist, type \"help\"");
|
|
}
|
|
}
|
|
}
|
|
|
|
_ = stream.write_all(b"EOF\n"); // BEL char
|
|
}
|
|
}
|
|
|
|
pub fn handle_daemon(config: Arc<config::Schema>) -> Option<anyhow::Result<JoinHandle<()>>> {
|
|
config.ipc.clone().map(|ipc_path| {
|
|
println!("starting ipc daemon at {ipc_path:#?}");
|
|
|
|
let listener = UnixListener::bind(&ipc_path).context("failed to bind to ipc socket")?;
|
|
ctrlc::try_set_handler(move || {
|
|
_ = fs::remove_file(&ipc_path);
|
|
process::exit(2);
|
|
})
|
|
.context("failed to set exit handler")?;
|
|
|
|
println!("ipc daemon started");
|
|
|
|
Ok(thread::spawn(move || {
|
|
for stream in listener.incoming() {
|
|
match stream {
|
|
Ok(stream) => {
|
|
handle_daemon_client(stream, &config);
|
|
}
|
|
Err(err) => eprintln!("ipc daemon error: {err:#?}"),
|
|
}
|
|
}
|
|
}))
|
|
})
|
|
}
|