Skip to content

Commit 754e6c7

Browse files
benthecarmanclaude
andcommitted
Add systemd readiness and stopping notifications
If NOTIFY_SOCKET is set, send READY=1 and STOPPING=1 to systemd. This enables proper dependency ordering and faster startup detection when running as a Type=notify service unit. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e3f6b83 commit 754e6c7

3 files changed

Lines changed: 73 additions & 0 deletions

File tree

ldk-server/src/main.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ use crate::service::NodeService;
5151
use crate::util::config::{load_config, ArgsConfig, ChainSource};
5252
use crate::util::logger::ServerLogger;
5353
use crate::util::proto_adapter::{forwarded_payment_to_proto, payment_to_proto};
54+
use crate::util::systemd;
5455
use crate::util::tls::get_or_generate_tls_config;
5556

5657
const API_KEY_FILE: &str = "api_key";
@@ -273,6 +274,8 @@ fn main() {
273274
let tls_acceptor = tokio_rustls::TlsAcceptor::from(Arc::new(server_config));
274275
info!("TLS enabled for REST service on {}", config_file.rest_service_addr);
275276

277+
systemd::notify_ready();
278+
276279
loop {
277280
select! {
278281
event = event_node.next_event_async() => {
@@ -449,6 +452,7 @@ fn main() {
449452
}
450453
});
451454

455+
systemd::notify_stopping();
452456
node.stop().expect("Shutdown should always succeed.");
453457
info!("Shutdown complete..");
454458
}

ldk-server/src/util/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@
1010
pub(crate) mod config;
1111
pub(crate) mod logger;
1212
pub(crate) mod proto_adapter;
13+
pub(crate) mod systemd;
1314
pub(crate) mod tls;

ldk-server/src/util/systemd.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// This file is Copyright its original authors, visible in version control
2+
// history.
3+
//
4+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5+
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7+
// You may not use this file except in accordance with one or both of these
8+
// licenses.
9+
10+
#[cfg(target_os = "linux")]
11+
use std::os::linux::net::SocketAddrExt;
12+
#[cfg(target_os = "linux")]
13+
use std::os::unix::net::UnixDatagram;
14+
15+
#[cfg(target_os = "linux")]
16+
use log::{info, warn};
17+
18+
#[cfg(target_os = "linux")]
19+
fn notify(state: &str) {
20+
let socket_path = match std::env::var("NOTIFY_SOCKET") {
21+
Ok(path) => path,
22+
Err(_) => return,
23+
};
24+
25+
let socket = match UnixDatagram::unbound() {
26+
Ok(s) => s,
27+
Err(e) => {
28+
warn!("Failed to create socket for systemd notification: {e}");
29+
return;
30+
},
31+
};
32+
33+
// systemd sets NOTIFY_SOCKET to either a filesystem path (e.g. /run/systemd/notify)
34+
// or an abstract socket prefixed with '@'. Abstract sockets require special addressing.
35+
let result = if let Some(abstract_name) = socket_path.strip_prefix('@') {
36+
match std::os::unix::net::SocketAddr::from_abstract_name(abstract_name) {
37+
Ok(addr) => socket.send_to_addr(state.as_bytes(), &addr),
38+
Err(e) => {
39+
warn!("Failed to create abstract socket address: {e}");
40+
return;
41+
},
42+
}
43+
} else {
44+
socket.send_to(state.as_bytes(), &socket_path)
45+
};
46+
47+
if let Err(e) = result {
48+
warn!("Failed to send systemd notification: {e}");
49+
} else {
50+
info!("Sent systemd notification: {state}");
51+
}
52+
}
53+
54+
#[cfg(target_os = "linux")]
55+
pub fn notify_ready() {
56+
notify("READY=1");
57+
}
58+
59+
#[cfg(target_os = "linux")]
60+
pub fn notify_stopping() {
61+
notify("STOPPING=1");
62+
}
63+
64+
#[cfg(not(target_os = "linux"))]
65+
pub fn notify_ready() {}
66+
67+
#[cfg(not(target_os = "linux"))]
68+
pub fn notify_stopping() {}

0 commit comments

Comments
 (0)