@@ -41,6 +41,48 @@ 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 command
45+ ///
46+ /// This function uses the sysctl command-line tool to retrieve the kernel
47+ /// boot time on macOS, avoiding any unsafe code. It parses the output
48+ /// of the sysctl command to extract the boot time.
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 std:: process:: Command ;
56+
57+ // Execute sysctl command to get boot time
58+ let output = Command :: new ( "sysctl" )
59+ . arg ( "-n" )
60+ . arg ( "kern.boottime" )
61+ . output ( ) ;
62+
63+ if let Ok ( output) = output {
64+ if output. status . success ( ) {
65+ // Parse output like: "Wed Oct 19 08:25:52 2025"
66+ // The actual format is: { sec = 1729338352, usec = 0 } Wed Oct 19 08:25:52 2025
67+ let stdout = String :: from_utf8_lossy ( & output. stdout ) ;
68+
69+ // Extract the seconds from the output
70+ // Look for "sec = " pattern
71+ if let Some ( sec_start) = stdout. find ( "sec = " ) {
72+ let sec_part = & stdout[ sec_start + 6 ..] ;
73+ if let Some ( sec_end) = sec_part. find ( ',' ) {
74+ let sec_str = & sec_part[ ..sec_end] ;
75+ if let Ok ( boot_time) = sec_str. trim ( ) . parse :: < i64 > ( ) {
76+ return Some ( boot_time as time_t ) ;
77+ }
78+ }
79+ }
80+ }
81+ }
82+
83+ None
84+ }
85+
4486/// Get the system uptime
4587///
4688/// # Arguments
@@ -135,38 +177,11 @@ pub fn get_uptime(boot_time: Option<time_t>) -> UResult<i64> {
135177 // This fallback only runs if utmpx failed to provide a boot time.
136178 #[ cfg( target_os = "macos" ) ]
137179 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-
142180 let mut t = derived_boot_time;
143181 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 ) ;
182+ // Use a safe wrapper function to get boot time via sysctl
183+ if let Some ( boot_time) = get_macos_boot_time_sysctl ( ) {
184+ t = Some ( boot_time) ;
170185 }
171186 }
172187 t
@@ -446,47 +461,24 @@ mod tests {
446461 #[ test]
447462 #[ cfg( target_os = "macos" ) ]
448463 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-
464+ // Test the safe wrapper function
465+ let boot_time = get_macos_boot_time_sysctl ( ) ;
466+
467+ // Verify the safe wrapper succeeded
468+ assert ! ( boot_time. is_some( ) , "get_macos_boot_time_sysctl should succeed on macOS" ) ;
469+
470+ let boot_time = boot_time. unwrap ( ) ;
471+
480472 // Verify boot time is valid (positive, reasonable value)
481- assert ! ( tv . tv_sec > 0 , "Boot time should be positive" ) ;
473+ assert ! ( boot_time > 0 , "Boot time should be positive" ) ;
482474
483475 // 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" ) ;
476+ assert ! ( boot_time > 946684800 , "Boot time should be after year 2000" ) ;
485477
486478 // Boot time should be before current time
487479 let now = chrono:: Local :: now ( ) . timestamp ( ) ;
488480 assert ! (
489- ( tv . tv_sec as i64 ) < now,
481+ ( boot_time as i64 ) < now,
490482 "Boot time should be before current time"
491483 ) ;
492484 }
0 commit comments