use std::io::{BufRead, BufReader, Write};
use std::net::{TcpListener, TcpStream};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
use blivet::{daemonize, DaemonConfig};
const ADDR: &str = "127.0.0.1:7878";
const PIDFILE: &str = "/tmp/echo_server.pid";
const LOGFILE: &str = "/tmp/echo_server.log";
fn main() -> Result<(), Box<dyn std::error::Error>> {
let foreground = std::env::args().any(|a| a == "--foreground");
let mut config = DaemonConfig::new();
config
.pidfile(PIDFILE)
.stdout(LOGFILE)
.stderr(LOGFILE)
.foreground(foreground);
let mut ctx = unsafe { daemonize(&config)? };
let listener = TcpListener::bind(ADDR)?;
println!("echo_server listening on {ADDR}");
ctx.notify_parent()?;
let shutdown = Arc::new(AtomicBool::new(false));
signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&shutdown))?;
signal_hook::flag::register(signal_hook::consts::SIGINT, Arc::clone(&shutdown))?;
listener.set_nonblocking(true)?;
while !shutdown.load(Ordering::Relaxed) {
match listener.accept() {
Ok((stream, peer)) => {
println!("connection from {peer}");
if let Err(e) = stream.set_nonblocking(false) {
eprintln!("failed to reset socket to blocking: {e}");
continue;
}
std::thread::spawn(move || {
if let Err(e) = handle_client(stream) {
eprintln!("client error: {e}");
}
});
}
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
std::thread::sleep(Duration::from_millis(100));
}
Err(e) => eprintln!("accept error: {e}"),
}
}
println!("shutting down, removing pidfile");
ctx.cleanup();
Ok(())
}
fn handle_client(stream: TcpStream) -> std::io::Result<()> {
let mut writer = stream.try_clone()?;
let reader = BufReader::new(stream);
for line in reader.lines() {
let line = line?;
writeln!(writer, "{line}")?;
writer.flush()?;
}
Ok(())
}