Skip to content

Commit 67839ab

Browse files
committed
fix(sort): use native sysctl for percentage buffer-size on BSD platforms
Replace subprocess-based sysctl calls with native libc::sysctl() syscalls for macOS, FreeBSD, OpenBSD, and NetBSD to fix test_buffer_sizes failure in CI environments. The previous implementation spawned sysctl subprocesses which could fail in sandboxed GitHub Actions runners. The native approach is more reliable, performant, and includes subprocess fallback for robustness. Fixes percentage-based buffer size parsing (e.g., sort -S 10%)
1 parent 87f02ec commit 67839ab

File tree

1 file changed

+113
-4
lines changed

1 file changed

+113
-4
lines changed

src/uucore/src/lib/features/parser/parse_size.rs

Lines changed: 113 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,34 @@ pub fn available_memory_bytes() -> Option<u128> {
7979

8080
/// Get the total number of bytes of physical memory on macOS.
8181
///
82-
/// Uses the `sysctl` command to query `hw.memsize`.
82+
/// Uses native `sysctl` syscall to query `hw.memsize`, with subprocess fallback.
8383
#[cfg(target_os = "macos")]
8484
fn total_physical_memory() -> Result<u128, SystemError> {
85+
use nix::libc;
86+
use std::mem;
87+
use std::ptr;
88+
89+
// Try native sysctl syscall first (more reliable in restricted environments)
90+
let mut size: u64 = 0;
91+
let mut len = mem::size_of::<u64>();
92+
let mut mib = [libc::CTL_HW, libc::HW_MEMSIZE];
93+
94+
let result = unsafe {
95+
libc::sysctl(
96+
mib.as_mut_ptr(),
97+
2,
98+
ptr::from_mut(&mut size).cast::<libc::c_void>(),
99+
&mut len,
100+
ptr::null_mut(),
101+
0,
102+
)
103+
};
104+
105+
if result == 0 {
106+
return Ok(size as u128);
107+
}
108+
109+
// Fallback to subprocess if native call fails
85110
let output = std::process::Command::new("sysctl")
86111
.arg("-n")
87112
.arg("hw.memsize")
@@ -94,9 +119,37 @@ fn total_physical_memory() -> Result<u128, SystemError> {
94119

95120
/// Get the total number of bytes of physical memory on FreeBSD.
96121
///
97-
/// Uses the `sysctl` command to query `hw.physmem`.
122+
/// Uses native `sysctl` syscall to query `hw.physmem`, with subprocess fallback.
98123
#[cfg(target_os = "freebsd")]
99124
fn total_physical_memory() -> Result<u128, SystemError> {
125+
use nix::libc;
126+
use std::mem;
127+
use std::ptr;
128+
129+
// HW_PHYSMEM constant (not always exported by libc)
130+
const HW_PHYSMEM: libc::c_int = 5;
131+
132+
// Try native sysctl syscall first (more reliable in restricted environments)
133+
let mut size: u64 = 0;
134+
let mut len = mem::size_of::<u64>();
135+
let mut mib = [libc::CTL_HW, HW_PHYSMEM];
136+
137+
let result = unsafe {
138+
libc::sysctl(
139+
mib.as_mut_ptr(),
140+
2,
141+
ptr::from_mut(&mut size).cast::<libc::c_void>(),
142+
&mut len,
143+
ptr::null_mut(),
144+
0,
145+
)
146+
};
147+
148+
if result == 0 {
149+
return Ok(size as u128);
150+
}
151+
152+
// Fallback to subprocess if native call fails
100153
let output = std::process::Command::new("sysctl")
101154
.arg("-n")
102155
.arg("hw.physmem")
@@ -109,9 +162,37 @@ fn total_physical_memory() -> Result<u128, SystemError> {
109162

110163
/// Get the total number of bytes of physical memory on OpenBSD.
111164
///
112-
/// Uses the `sysctl` command to query `hw.physmem`.
165+
/// Uses native `sysctl` syscall to query `hw.physmem`, with subprocess fallback.
113166
#[cfg(target_os = "openbsd")]
114167
fn total_physical_memory() -> Result<u128, SystemError> {
168+
use nix::libc;
169+
use std::mem;
170+
use std::ptr;
171+
172+
// HW_PHYSMEM constant (not always exported by libc)
173+
const HW_PHYSMEM: libc::c_int = 19;
174+
175+
// Try native sysctl syscall first (more reliable in restricted environments)
176+
let mut size: u64 = 0;
177+
let mut len = mem::size_of::<u64>();
178+
let mut mib = [libc::CTL_HW, HW_PHYSMEM];
179+
180+
let result = unsafe {
181+
libc::sysctl(
182+
mib.as_mut_ptr(),
183+
2,
184+
ptr::from_mut(&mut size).cast::<libc::c_void>(),
185+
&mut len,
186+
ptr::null_mut(),
187+
0,
188+
)
189+
};
190+
191+
if result == 0 {
192+
return Ok(size as u128);
193+
}
194+
195+
// Fallback to subprocess if native call fails
115196
let output = std::process::Command::new("sysctl")
116197
.arg("-n")
117198
.arg("hw.physmem")
@@ -124,9 +205,37 @@ fn total_physical_memory() -> Result<u128, SystemError> {
124205

125206
/// Get the total number of bytes of physical memory on NetBSD.
126207
///
127-
/// Uses the `sysctl` command to query `hw.physmem64`.
208+
/// Uses native `sysctl` syscall to query `hw.physmem64`, with subprocess fallback.
128209
#[cfg(target_os = "netbsd")]
129210
fn total_physical_memory() -> Result<u128, SystemError> {
211+
use nix::libc;
212+
use std::mem;
213+
use std::ptr;
214+
215+
// HW_PHYSMEM64 constant (not always exported by libc)
216+
const HW_PHYSMEM64: libc::c_int = 9;
217+
218+
// Try native sysctl syscall first (more reliable in restricted environments)
219+
let mut size: u64 = 0;
220+
let mut len = mem::size_of::<u64>();
221+
let mut mib = [libc::CTL_HW, HW_PHYSMEM64];
222+
223+
let result = unsafe {
224+
libc::sysctl(
225+
mib.as_mut_ptr(),
226+
2,
227+
ptr::from_mut(&mut size).cast::<libc::c_void>(),
228+
&mut len,
229+
ptr::null_mut(),
230+
0,
231+
)
232+
};
233+
234+
if result == 0 {
235+
return Ok(size as u128);
236+
}
237+
238+
// Fallback to subprocess if native call fails
130239
let output = std::process::Command::new("sysctl")
131240
.arg("-n")
132241
.arg("hw.physmem64")

0 commit comments

Comments
 (0)