@@ -41,6 +41,53 @@ pub fn get_formatted_time() -> String {
4141 Local :: now ( ) . time ( ) . format ( "%H:%M:%S" ) . to_string ( )
4242}
4343
44+ /// Safely get macOS boot time using sysctl kern.boottime
45+ ///
46+ /// This function provides a safe wrapper around the sysctl system call
47+ /// to retrieve the kernel boot time on macOS. It encapsulates the unsafe
48+ /// behavior and provides a safe API that returns Option<time_t>.
49+ ///
50+ /// # Returns
51+ ///
52+ /// Returns Some(time_t) if successful, None if the call fails.
53+ #[ cfg( target_os = "macos" ) ]
54+ fn get_macos_boot_time_sysctl ( ) -> Option < time_t > {
55+ use libc:: { c_int, c_void, size_t, timeval} ;
56+ use std:: mem:: size_of;
57+
58+ // MIB for kern.boottime - the macOS-specific way to get boot time
59+ let mut mib: [ c_int ; 2 ] = [ libc:: CTL_KERN , libc:: KERN_BOOTTIME ] ;
60+ let mut tv: timeval = timeval {
61+ tv_sec : 0 ,
62+ tv_usec : 0 ,
63+ } ;
64+ let mut tv_len: size_t = size_of :: < timeval > ( ) as size_t ;
65+
66+ // SAFETY: This unsafe block is isolated and carefully controlled:
67+ // - mib is a valid 2-element array for kern.boottime
68+ // - tv is a properly initialized timeval struct
69+ // - tv_len correctly reflects the size of tv
70+ // - All pointers are valid and properly aligned
71+ // - We check the return value before using the result
72+ // - The function returns None on any error, providing a safe API
73+ let ret = unsafe {
74+ libc:: sysctl (
75+ mib. as_mut_ptr ( ) ,
76+ 2u32 , // namelen
77+ std:: ptr:: from_mut :: < timeval > ( & mut tv) as * mut c_void ,
78+ std:: ptr:: from_mut :: < size_t > ( & mut tv_len) ,
79+ std:: ptr:: null_mut ( ) ,
80+ 0 ,
81+ )
82+ } ;
83+
84+ if ret == 0 && tv. tv_sec > 0 {
85+ Some ( tv. tv_sec as time_t )
86+ } else {
87+ None
88+ }
89+ }
90+
4491/// Get the system uptime
4592///
4693/// # Arguments
@@ -135,38 +182,11 @@ pub fn get_uptime(boot_time: Option<time_t>) -> UResult<i64> {
135182 // This fallback only runs if utmpx failed to provide a boot time.
136183 #[ cfg( target_os = "macos" ) ]
137184 let derived_boot_time = {
138- use libc:: { c_int, c_void, size_t, sysctl, timeval} ;
139- use std:: mem:: size_of;
140- use std:: ptr;
141-
142185 let mut t = derived_boot_time;
143186 if t. is_none ( ) {
144- // MIB for kern.boottime - the macOS-specific way to get boot time
145- let mut mib: [ c_int ; 2 ] = [ libc:: CTL_KERN , libc:: KERN_BOOTTIME ] ;
146- let mut tv: timeval = timeval {
147- tv_sec : 0 ,
148- tv_usec : 0 ,
149- } ;
150- let mut tv_len: size_t = size_of :: < timeval > ( ) as size_t ;
151-
152- // SAFETY: We're calling sysctl with valid parameters:
153- // - mib is a valid 2-element array for kern.boottime
154- // - tv is a properly initialized timeval struct
155- // - tv_len correctly reflects the size of tv
156- // - All pointers are valid and properly aligned
157- // - We check the return value before using the result
158- let ret = unsafe {
159- sysctl (
160- mib. as_mut_ptr ( ) ,
161- 2u32 , // namelen
162- std:: ptr:: from_mut :: < timeval > ( & mut tv) as * mut c_void ,
163- std:: ptr:: from_mut :: < size_t > ( & mut tv_len) ,
164- ptr:: null_mut ( ) ,
165- 0 ,
166- )
167- } ;
168- if ret == 0 && tv. tv_sec > 0 {
169- t = Some ( tv. tv_sec as time_t ) ;
187+ // Use a safe wrapper function to get boot time via sysctl
188+ if let Some ( boot_time) = get_macos_boot_time_sysctl ( ) {
189+ t = Some ( boot_time) ;
170190 }
171191 }
172192 t
@@ -446,47 +466,24 @@ mod tests {
446466 #[ test]
447467 #[ cfg( target_os = "macos" ) ]
448468 fn test_macos_sysctl_boottime_available ( ) {
449- use libc:: { c_int, c_void, size_t, sysctl, timeval} ;
450- use std:: mem:: size_of;
451- use std:: ptr;
452-
453- // Attempt to get boot time directly via sysctl
454- let mut mib: [ c_int ; 2 ] = [ libc:: CTL_KERN , libc:: KERN_BOOTTIME ] ;
455- let mut tv: timeval = timeval {
456- tv_sec : 0 ,
457- tv_usec : 0 ,
458- } ;
459- let mut tv_len: size_t = size_of :: < timeval > ( ) as size_t ;
460-
461- // SAFETY: We're calling sysctl with valid parameters:
462- // - mib is a valid 2-element array for kern.boottime
463- // - tv is a properly initialized timeval struct
464- // - tv_len correctly reflects the size of tv
465- // - We check the return value before using the result
466- let ret = unsafe {
467- sysctl (
468- mib. as_mut_ptr ( ) ,
469- 2u32 ,
470- std:: ptr:: from_mut :: < timeval > ( & mut tv) as * mut c_void ,
471- std:: ptr:: from_mut :: < size_t > ( & mut tv_len) ,
472- ptr:: null_mut ( ) ,
473- 0 ,
474- )
475- } ;
476-
477- // Verify sysctl succeeded
478- assert_eq ! ( ret, 0 , "sysctl kern.boottime should succeed on macOS" ) ;
479-
469+ // Test the safe wrapper function
470+ let boot_time = get_macos_boot_time_sysctl ( ) ;
471+
472+ // Verify the safe wrapper succeeded
473+ assert ! ( boot_time. is_some( ) , "get_macos_boot_time_sysctl should succeed on macOS" ) ;
474+
475+ let boot_time = boot_time. unwrap ( ) ;
476+
480477 // Verify boot time is valid (positive, reasonable value)
481- assert ! ( tv . tv_sec > 0 , "Boot time should be positive" ) ;
478+ assert ! ( boot_time > 0 , "Boot time should be positive" ) ;
482479
483480 // Boot time should be after 2000-01-01 (946684800 seconds since epoch)
484- assert ! ( tv . tv_sec > 946684800 , "Boot time should be after year 2000" ) ;
481+ assert ! ( boot_time > 946684800 , "Boot time should be after year 2000" ) ;
485482
486483 // Boot time should be before current time
487484 let now = chrono:: Local :: now ( ) . timestamp ( ) ;
488485 assert ! (
489- ( tv . tv_sec as i64 ) < now,
486+ ( boot_time as i64 ) < now,
490487 "Boot time should be before current time"
491488 ) ;
492489 }
0 commit comments