mirror of
https://github.com/atuinsh/atuin.git
synced 2025-06-21 02:18:17 +02:00
feat: support systemd socket activation for daemon (#2039)
This avoids issues with clients attempting to connect to the daemon while it's starting, systemd creates the socket early and will queue connections up until the daemon is ready to accept them.
This commit is contained in:
parent
40543eb8f9
commit
2e88321aec
20
Cargo.lock
generated
20
Cargo.lock
generated
@ -326,6 +326,7 @@ dependencies = [
|
||||
"atuin-history",
|
||||
"dashmap",
|
||||
"eyre",
|
||||
"listenfd",
|
||||
"prost",
|
||||
"prost-types",
|
||||
"rand",
|
||||
@ -2080,6 +2081,17 @@ version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||
|
||||
[[package]]
|
||||
name = "listenfd"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0500463acd96259d219abb05dc57e5a076ef04b2db9a2112846929b5f174c96"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"uuid",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.11"
|
||||
@ -3975,9 +3987,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.34"
|
||||
version = "0.3.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
|
||||
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
@ -3998,9 +4010,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.17"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774"
|
||||
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
|
@ -19,7 +19,7 @@ readme = "README.md"
|
||||
async-trait = "0.1.58"
|
||||
base64 = "0.22"
|
||||
log = "0.4"
|
||||
time = { version = "=0.3.34", features = [
|
||||
time = { version = "0.3.36", features = [
|
||||
"serde-human-readable",
|
||||
"macros",
|
||||
"local-offset",
|
||||
|
@ -223,5 +223,10 @@ records = true
|
||||
## windows: Not Supported
|
||||
# socket_path = "~/.local/share/atuin/atuin.sock"
|
||||
|
||||
## Use systemd socket activation rather than opening the given path (the path must still be correct for the client)
|
||||
## linux: false
|
||||
## mac/windows: Not Supported
|
||||
# systemd_socket = false
|
||||
|
||||
## The port that should be used for TCP on non unix systems
|
||||
# tcp_port = 8889
|
||||
|
@ -350,6 +350,9 @@ pub struct Daemon {
|
||||
/// The path to the unix socket used by the daemon
|
||||
pub socket_path: String,
|
||||
|
||||
/// Use a socket passed via systemd's socket activation protocol, instead of the path
|
||||
pub systemd_socket: bool,
|
||||
|
||||
/// The port that should be used for TCP on non unix systems
|
||||
pub tcp_port: u64,
|
||||
}
|
||||
@ -368,6 +371,7 @@ impl Default for Daemon {
|
||||
enabled: false,
|
||||
sync_frequency: 300,
|
||||
socket_path: "".to_string(),
|
||||
systemd_socket: false,
|
||||
tcp_port: 8889,
|
||||
}
|
||||
}
|
||||
@ -715,6 +719,7 @@ impl Settings {
|
||||
.set_default("daemon.sync_frequency", 300)?
|
||||
.set_default("daemon.enabled", false)?
|
||||
.set_default("daemon.socket_path", socket_path.to_str())?
|
||||
.set_default("daemon.systemd_socket", false)?
|
||||
.set_default("daemon.tcp_port", 8889)?
|
||||
.set_default(
|
||||
"prefers_reduced_motion",
|
||||
|
@ -32,5 +32,8 @@ prost-types = "0.12"
|
||||
tokio-stream = {version="0.1.14", features=["net"]}
|
||||
rand.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
listenfd = "1.0.1"
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build = "0.11"
|
||||
|
@ -133,7 +133,7 @@ impl HistorySvc for HistoryService {
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
async fn shutdown_signal(socket: PathBuf) {
|
||||
async fn shutdown_signal(socket: Option<PathBuf>) {
|
||||
let mut term = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
|
||||
.expect("failed to register sigterm handler");
|
||||
let mut int = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::interrupt())
|
||||
@ -145,7 +145,9 @@ async fn shutdown_signal(socket: PathBuf) {
|
||||
}
|
||||
|
||||
eprintln!("Removing socket...");
|
||||
if let Some(socket) = socket {
|
||||
std::fs::remove_file(socket).expect("failed to remove socket");
|
||||
}
|
||||
eprintln!("Shutting down...");
|
||||
}
|
||||
|
||||
@ -163,15 +165,54 @@ async fn start_server(settings: Settings, history: HistoryService) -> Result<()>
|
||||
use tokio::net::UnixListener;
|
||||
use tokio_stream::wrappers::UnixListenerStream;
|
||||
|
||||
let socket = settings.daemon.socket_path.clone();
|
||||
let socket_path = settings.daemon.socket_path;
|
||||
|
||||
let (uds, cleanup) = if cfg!(target_os = "linux") && settings.daemon.systemd_socket {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
use eyre::OptionExt;
|
||||
tracing::info!("getting systemd socket");
|
||||
let listener = listenfd::ListenFd::from_env()
|
||||
.take_unix_listener(0)?
|
||||
.ok_or_eyre("missing systemd socket")?;
|
||||
listener.set_nonblocking(true)?;
|
||||
let actual_path = listener
|
||||
.local_addr()
|
||||
.context("getting systemd socket's path")
|
||||
.and_then(|addr| {
|
||||
addr.as_pathname()
|
||||
.ok_or_eyre("systemd socket missing path")
|
||||
.map(|path| path.to_owned())
|
||||
});
|
||||
match actual_path {
|
||||
Ok(actual_path) => {
|
||||
tracing::info!("listening on systemd socket: {actual_path:?}");
|
||||
if actual_path != std::path::Path::new(&socket_path) {
|
||||
tracing::warn!(
|
||||
"systemd socket is not at configured client path: {socket_path:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::warn!("could not detect systemd socket path, ensure that it's at the configured path: {socket_path:?}, error: {err:?}");
|
||||
}
|
||||
}
|
||||
(UnixListener::from_std(listener)?, false)
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
unreachable!()
|
||||
} else {
|
||||
tracing::info!("listening on unix socket {socket_path:?}");
|
||||
(UnixListener::bind(socket_path.clone())?, true)
|
||||
};
|
||||
|
||||
let uds = UnixListener::bind(socket.clone())?;
|
||||
let uds_stream = UnixListenerStream::new(uds);
|
||||
|
||||
tracing::info!("listening on unix socket {:?}", socket);
|
||||
Server::builder()
|
||||
.add_service(HistoryServer::new(history))
|
||||
.serve_with_incoming_shutdown(uds_stream, shutdown_signal(socket.into()))
|
||||
.serve_with_incoming_shutdown(
|
||||
uds_stream,
|
||||
shutdown_signal(cleanup.then_some(socket_path.into())),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ eyre = "0.6"
|
||||
tauri = { version = "2.0.0-beta", features = ["tray-icon"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
time = "0.3.34"
|
||||
time = "0.3.36"
|
||||
uuid = "1.7.0"
|
||||
syntect = "5.2.0"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user