Skip to content

Commit 8eb2c5c

Browse files
authored
Add NotifyState::MonotonicUsec and helper
2 parents 6990e37 + 5462699 commit 8eb2c5c

File tree

5 files changed

+64
-29
lines changed

5 files changed

+64
-29
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## [0.4.4] - 2025-01-16
4+
5+
### Added
6+
7+
- added `NotifyState::MonotonicUsec`, for use with `Type=notify-reload`
8+
39
## [0.4.3] - 2024-10-05
410

511
### Added

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ fdstore = ["dep:sendfd"]
1717

1818
[dependencies]
1919
sendfd = { version = "0.4", optional = true }
20+
libc = "0.2"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
[API docs]: https://docs.rs/sd-notify/badge.svg
1010
[docs.rs]: https://docs.rs/sd-notify/
1111

12-
A lightweight (no dependencies) crate for sending `systemd` service state notifications.
12+
A lightweight crate for sending `systemd` service state notifications.
1313

1414
## Quick start
1515

src/ffi.rs

Lines changed: 0 additions & 10 deletions
This file was deleted.

src/lib.rs

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,26 @@ use std::env;
2727
use std::fmt::{self, Display, Formatter, Write};
2828
use std::fs;
2929
use std::io::{self, ErrorKind};
30+
use std::mem::MaybeUninit;
3031
#[cfg(feature = "fdstore")]
3132
use std::os::fd::BorrowedFd;
3233
use std::os::unix::io::RawFd;
3334
use std::os::unix::net::UnixDatagram;
3435
use std::process;
3536
use std::str::FromStr;
3637

37-
mod ffi;
38+
use libc::CLOCK_MONOTONIC;
3839

3940
/// Daemon notification for the service manager.
4041
#[derive(Clone, Debug)]
4142
pub enum NotifyState<'a> {
4243
/// Service startup is finished.
4344
Ready,
4445
/// Service is reloading its configuration.
46+
///
47+
/// On systemd v253 and newer, this message MUST be followed by a
48+
/// [`NotifyState::MonotonicUsec`] notification, or the reload will fail
49+
/// and the service will be terminated.
4550
Reloading,
4651
/// Service is stopping.
4752
Stopping,
@@ -70,6 +75,9 @@ pub enum NotifyState<'a> {
7075
/// Tells the service manager to use this name for the attached file descriptor.
7176
#[cfg(feature = "fdstore")]
7277
FdName(&'a str),
78+
/// Notify systemd of the current monotonic time in microseconds.
79+
/// You can construct this value by calling [`NotifyState::monotonic_usec_now()`].
80+
MonotonicUsec(i128),
7381
/// Custom state.
7482
Custom(&'a str),
7583
}
@@ -94,11 +102,27 @@ impl Display for NotifyState<'_> {
94102
NotifyState::FdStoreRemove => write!(f, "FDSTOREREMOVE=1"),
95103
#[cfg(feature = "fdstore")]
96104
NotifyState::FdName(name) => write!(f, "FDNAME={}", name),
105+
NotifyState::MonotonicUsec(usec) => write!(f, "MONOTONIC_USEC={}", usec),
97106
NotifyState::Custom(state) => write!(f, "{}", state),
98107
}
99108
}
100109
}
101110

111+
impl NotifyState<'_> {
112+
/// Create a new [`NotifyState::MonotonicUsec`] using the current system monotonic time.
113+
///
114+
/// # Example
115+
///
116+
/// ```
117+
/// # use sd_notify::NotifyState;
118+
/// #
119+
/// let _ = NotifyState::monotonic_usec_now().expect("Failed to read monotonic time");
120+
/// ```
121+
pub fn monotonic_usec_now() -> io::Result<Self> {
122+
monotonic_time_usec().map(NotifyState::MonotonicUsec)
123+
}
124+
}
125+
102126
/// Checks whether the system has been booted by `systemd`.
103127
///
104128
/// This is implemented by verifying that a `/run/systemd/system` directory exists.
@@ -379,14 +403,14 @@ impl Drop for UnsetEnvGuard {
379403
}
380404

381405
fn fd_cloexec(fd: u32) -> io::Result<()> {
382-
let fd = RawFd::try_from(fd).map_err(|_| io::Error::from_raw_os_error(ffi::EBADF))?;
383-
let flags = unsafe { ffi::fcntl(fd, ffi::F_GETFD, 0) };
406+
let fd = RawFd::try_from(fd).map_err(|_| io::Error::from_raw_os_error(libc::EBADF))?;
407+
let flags = unsafe { libc::fcntl(fd, libc::F_GETFD, 0) };
384408
if flags < 0 {
385409
return Err(io::Error::last_os_error());
386410
}
387-
let new_flags = flags | ffi::FD_CLOEXEC;
411+
let new_flags = flags | libc::FD_CLOEXEC;
388412
if new_flags != flags {
389-
let r = unsafe { ffi::fcntl(fd, ffi::F_SETFD, new_flags) };
413+
let r = unsafe { libc::fcntl(fd, libc::F_SETFD, new_flags) };
390414
if r < 0 {
391415
return Err(io::Error::last_os_error());
392416
}
@@ -445,6 +469,21 @@ pub fn watchdog_enabled(unset_env: bool, usec: &mut u64) -> bool {
445469
}
446470
}
447471

472+
fn monotonic_time_usec() -> io::Result<i128> {
473+
let mut timespec = MaybeUninit::uninit();
474+
let rv = unsafe { libc::clock_gettime(CLOCK_MONOTONIC, timespec.as_mut_ptr()) };
475+
if rv != 0 {
476+
return Err(io::Error::last_os_error());
477+
}
478+
let timespec = unsafe { timespec.assume_init() };
479+
480+
// nanoseconds / 1_000 -> microseconds.
481+
let lower_msec = (timespec.tv_nsec / 1_000) as i128;
482+
// seconds * 1_000_000 -> microseconds
483+
let upper_msec = (timespec.tv_sec * 1_000_000) as i128;
484+
Ok(upper_msec + lower_msec)
485+
}
486+
448487
#[cfg(test)]
449488
mod tests {
450489
use super::NotifyState;
@@ -488,14 +527,11 @@ mod tests {
488527
assert_eq!(s.recv_string(), "READY=1\n");
489528
assert!(env::var_os("NOTIFY_SOCKET").is_some());
490529

491-
super::notify(
492-
true,
493-
&[
494-
NotifyState::Status("Reticulating splines"),
495-
NotifyState::Watchdog,
496-
NotifyState::Custom("X_WORKS=1"),
497-
],
498-
)
530+
super::notify(true, &[
531+
NotifyState::Status("Reticulating splines"),
532+
NotifyState::Watchdog,
533+
NotifyState::Custom("X_WORKS=1"),
534+
])
499535
.unwrap();
500536
assert_eq!(
501537
s.recv_string(),
@@ -565,11 +601,13 @@ mod tests {
565601
);
566602

567603
// Raise an error if LISTEN_FDNAMES has a different number of entries as fds
568-
assert!(super::zip_fds_with_names(
569-
3 as RawFd..6 as RawFd,
570-
Some("omelette:baguette".to_string())
571-
)
572-
.is_err());
604+
assert!(
605+
super::zip_fds_with_names(
606+
3 as RawFd..6 as RawFd,
607+
Some("omelette:baguette".to_string())
608+
)
609+
.is_err()
610+
);
573611
}
574612

575613
#[test]

0 commit comments

Comments
 (0)