Commit 61c6e94355d9
tarball2 files changed+49-9
@@ -1,10 +1,16 @@
11
use std::{22
collections::BTreeMap,33
fs, io,4
- os::unix::net::{UnixListener, UnixStream},4
+ os::{5
+ fd::FromRawFd,6
+ unix::net::{UnixListener, UnixStream},7
+ },58
path::PathBuf,69
process,7
- sync::{Arc, Mutex, MutexGuard},10
+ sync::{11
+ Arc, Mutex, MutexGuard,12
+ atomic::{AtomicBool, Ordering},13
+ },814
thread,915
time::{Duration, Instant},1016
};@@ -489,6 +495,11 @@ fn tick(shared: &Shared) {
489495
st.fire_overdue();490496
if st.conns == 0 && st.leases.is_empty() && st.idle_since.elapsed() >= st.cfg.queue.linger.0491497
{498
+ // systemd owns the socket and queues whoever knocks while we're499
+ // leaving, so there's nothing to tidy500
+ if ACTIVATED.load(Ordering::Relaxed) {501
+ process::exit(0);502
+ }492503
// a spawner holds this while it clears a stale socket, so taking it493504
// here stops us unlinking our successor's socket on the way out494505
if let Some(_guard) = try_spawn_lock() {@@ -558,15 +569,30 @@ fn watch_config(shared: &Shared) {
558569
}559570
}560571
572
+/// creates the file if it's gone, else a wiped runtime dir leaves us idling573
+/// forever unable to take the lock we need to exit561574
fn try_spawn_lock() -> Option<client::Flock> {562575
let file = fs::OpenOptions::new()576
+ .create(true)577
+ .truncate(false)563578
.write(true)564579
.open(paths::spawn_lock())565580
.ok()?;566581
client::Flock::try_exclusive(file)567582
}568583
569
-pub fn main(cfg: Config) -> ! {584
+static ACTIVATED: AtomicBool = AtomicBool::new(false);585
+586
+fn inherited() -> Option<UnixListener> {587
+ let ours = std::env::var("LISTEN_PID").ok()?.parse::<u32>().ok()? == process::id();588
+ if !ours || std::env::var("LISTEN_FDS").ok()? != "1" {589
+ return None;590
+ }591
+ // SAFETY: sd_listen_fds protocol, fd 3 is the listening socket592
+ Some(unsafe { UnixListener::from_raw_fd(3) })593
+}594
+595
+fn bind() -> UnixListener {570596
// fork once more so the client that spawned us can reap its child now571597
// instead of carrying a zombie for the length of its build572598
match unsafe { libc::fork() } {@@ -577,8 +603,7 @@ pub fn main(cfg: Config) -> ! {
577603
0 => {}578604
_ => process::exit(0),579605
}580
-581
- let listener = match UnixListener::bind(paths::socket()) {606
+ match UnixListener::bind(paths::socket()) {582607
Ok(l) => l,583608
Err(e) if e.kind() == io::ErrorKind::AddrInUse && client::connect().is_some() => {584609
process::exit(0)@@ -587,6 +612,15 @@ pub fn main(cfg: Config) -> ! {
587612
eprintln!("roundabout: bind: {e}");588613
process::exit(1);589614
}615
+ }616
+}617
+618
+pub fn main(cfg: Config) -> ! {619
+ let listener = if let Some(l) = inherited() {620
+ ACTIVATED.store(true, Ordering::Relaxed);621
+ l622
+ } else {623
+ bind()590624
};591625
592626
let shared: Shared = Arc::new(Mutex::new(State::new(cfg)));@@ -16,10 +16,16 @@ fn xdg(var: &str, fallback: &str) -> PathBuf {
1616
}1717
1818
pub fn config_file() -> PathBuf {19
- env::var_os("ROUNDABOUT_CONFIG").map_or_else(20
- || xdg("XDG_CONFIG_HOME", ".config").join("roundabout/config.toml"),21
- PathBuf::from,22
- )19
+ if let Some(p) = env::var_os("ROUNDABOUT_CONFIG") {20
+ return p.into();21
+ }22
+ let user = xdg("XDG_CONFIG_HOME", ".config").join("roundabout/config.toml");23
+ let system = PathBuf::from("/etc/roundabout/config.toml");24
+ if !user.exists() && system.exists() {25
+ system26
+ } else {27
+ user28
+ }2329
}2430
2531
pub fn state_dir() -> PathBuf {