@@ -27,21 +27,26 @@ use std::env;
27
27
use std:: fmt:: { self , Display , Formatter , Write } ;
28
28
use std:: fs;
29
29
use std:: io:: { self , ErrorKind } ;
30
+ use std:: mem:: MaybeUninit ;
30
31
#[ cfg( feature = "fdstore" ) ]
31
32
use std:: os:: fd:: BorrowedFd ;
32
33
use std:: os:: unix:: io:: RawFd ;
33
34
use std:: os:: unix:: net:: UnixDatagram ;
34
35
use std:: process;
35
36
use std:: str:: FromStr ;
36
37
37
- mod ffi ;
38
+ use libc :: CLOCK_MONOTONIC ;
38
39
39
40
/// Daemon notification for the service manager.
40
41
#[ derive( Clone , Debug ) ]
41
42
pub enum NotifyState < ' a > {
42
43
/// Service startup is finished.
43
44
Ready ,
44
45
/// 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.
45
50
Reloading ,
46
51
/// Service is stopping.
47
52
Stopping ,
@@ -70,6 +75,9 @@ pub enum NotifyState<'a> {
70
75
/// Tells the service manager to use this name for the attached file descriptor.
71
76
#[ cfg( feature = "fdstore" ) ]
72
77
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 ) ,
73
81
/// Custom state.
74
82
Custom ( & ' a str ) ,
75
83
}
@@ -94,11 +102,27 @@ impl Display for NotifyState<'_> {
94
102
NotifyState :: FdStoreRemove => write ! ( f, "FDSTOREREMOVE=1" ) ,
95
103
#[ cfg( feature = "fdstore" ) ]
96
104
NotifyState :: FdName ( name) => write ! ( f, "FDNAME={}" , name) ,
105
+ NotifyState :: MonotonicUsec ( usec) => write ! ( f, "MONOTONIC_USEC={}" , usec) ,
97
106
NotifyState :: Custom ( state) => write ! ( f, "{}" , state) ,
98
107
}
99
108
}
100
109
}
101
110
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
+
102
126
/// Checks whether the system has been booted by `systemd`.
103
127
///
104
128
/// This is implemented by verifying that a `/run/systemd/system` directory exists.
@@ -379,14 +403,14 @@ impl Drop for UnsetEnvGuard {
379
403
}
380
404
381
405
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 ) } ;
384
408
if flags < 0 {
385
409
return Err ( io:: Error :: last_os_error ( ) ) ;
386
410
}
387
- let new_flags = flags | ffi :: FD_CLOEXEC ;
411
+ let new_flags = flags | libc :: FD_CLOEXEC ;
388
412
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) } ;
390
414
if r < 0 {
391
415
return Err ( io:: Error :: last_os_error ( ) ) ;
392
416
}
@@ -445,6 +469,21 @@ pub fn watchdog_enabled(unset_env: bool, usec: &mut u64) -> bool {
445
469
}
446
470
}
447
471
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
+
448
487
#[ cfg( test) ]
449
488
mod tests {
450
489
use super :: NotifyState ;
@@ -488,14 +527,11 @@ mod tests {
488
527
assert_eq ! ( s. recv_string( ) , "READY=1\n " ) ;
489
528
assert ! ( env:: var_os( "NOTIFY_SOCKET" ) . is_some( ) ) ;
490
529
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
+ ] )
499
535
. unwrap ( ) ;
500
536
assert_eq ! (
501
537
s. recv_string( ) ,
@@ -565,11 +601,13 @@ mod tests {
565
601
) ;
566
602
567
603
// 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
+ ) ;
573
611
}
574
612
575
613
#[ test]
0 commit comments