Skip to content

Commit 3fd7774

Browse files
committed
refactor(uucore): replace unsafe sysctl with safe wrapper function
- Extract sysctl kern.boottime logic into dedicated safe wrapper function - The get_macos_boot_time_sysctl() function encapsulates unsafe behavior - Provides a safe API that returns Option<time_t> instead of direct unsafe access - Updates test to use the new safe wrapper function - Addresses review feedback to remove inline unsafe block This change maintains the same functionality while improving code safety and encapsulation. The unsafe behavior is now isolated in a single well-documented function with proper safety invariants.
1 parent 256dbd3 commit 3fd7774

File tree

1 file changed

+61
-64
lines changed

1 file changed

+61
-64
lines changed

src/uucore/src/lib/features/uptime.rs

Lines changed: 61 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)