mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-29 12:07:46 +00:00
use std::command::pre_exec() to set limits on child before exec
This commit is contained in:
parent
dab02d005d
commit
db142f9449
2 changed files with 57 additions and 17 deletions
|
@ -495,10 +495,10 @@ rstest = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dev-dependencies]
|
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dev-dependencies]
|
||||||
procfs = { version = "0.16", default-features = false }
|
procfs = { version = "0.16", default-features = false }
|
||||||
rlimit = "0.10.1"
|
|
||||||
|
|
||||||
[target.'cfg(unix)'.dev-dependencies]
|
[target.'cfg(unix)'.dev-dependencies]
|
||||||
nix = { workspace = true, features = ["process", "signal", "user", "term"] }
|
nix = { workspace = true, features = ["process", "signal", "user", "term"] }
|
||||||
|
rlimit = "0.10.1"
|
||||||
rand_pcg = "0.3"
|
rand_pcg = "0.3"
|
||||||
xattr = { workspace = true }
|
xattr = { workspace = true }
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,16 @@
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
//spell-checker: ignore (linux) rlimit prlimit coreutil ggroups uchild uncaptured scmd SHLVL canonicalized openpty winsize xpixel ypixel
|
//spell-checker: ignore (linux) rlimit prlimit coreutil ggroups uchild uncaptured scmd SHLVL canonicalized openpty
|
||||||
|
//spell-checker: ignore (linux) winsize xpixel ypixel setrlimit FSIZE
|
||||||
|
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use nix::pty::OpenptyResult;
|
use nix::pty::OpenptyResult;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(unix)]
|
||||||
use rlimit::prlimit;
|
use rlimit::setrlimit;
|
||||||
#[cfg(feature = "sleep")]
|
#[cfg(feature = "sleep")]
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
@ -27,6 +28,8 @@ use std::os::fd::OwnedFd;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::os::unix::fs::{symlink as symlink_dir, symlink as symlink_file, PermissionsExt};
|
use std::os::unix::fs::{symlink as symlink_dir, symlink as symlink_file, PermissionsExt};
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
use std::os::unix::process::CommandExt;
|
||||||
|
#[cfg(unix)]
|
||||||
use std::os::unix::process::ExitStatusExt;
|
use std::os::unix::process::ExitStatusExt;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use std::os::windows::fs::{symlink_dir, symlink_file};
|
use std::os::windows::fs::{symlink_dir, symlink_file};
|
||||||
|
@ -1224,7 +1227,7 @@ pub struct UCommand {
|
||||||
stdout: Option<Stdio>,
|
stdout: Option<Stdio>,
|
||||||
stderr: Option<Stdio>,
|
stderr: Option<Stdio>,
|
||||||
bytes_into_stdin: Option<Vec<u8>>,
|
bytes_into_stdin: Option<Vec<u8>>,
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(unix)]
|
||||||
limits: Vec<(rlimit::Resource, u64, u64)>,
|
limits: Vec<(rlimit::Resource, u64, u64)>,
|
||||||
stderr_to_stdout: bool,
|
stderr_to_stdout: bool,
|
||||||
timeout: Option<Duration>,
|
timeout: Option<Duration>,
|
||||||
|
@ -1387,7 +1390,7 @@ impl UCommand {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(unix)]
|
||||||
pub fn limit(
|
pub fn limit(
|
||||||
&mut self,
|
&mut self,
|
||||||
resource: rlimit::Resource,
|
resource: rlimit::Resource,
|
||||||
|
@ -1646,6 +1649,25 @@ impl UCommand {
|
||||||
command.stdin(pi_slave).stdout(po_slave).stderr(pe_slave);
|
command.stdin(pi_slave).stdout(po_slave).stderr(pe_slave);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
if !self.limits.is_empty() {
|
||||||
|
// just to be safe: move a copy of the limits list into the closure.
|
||||||
|
// this way the closure is fully self-contained.
|
||||||
|
let limits_copy = self.limits.clone();
|
||||||
|
let closure = move || -> Result<()> {
|
||||||
|
for &(resource, soft_limit, hard_limit) in &limits_copy {
|
||||||
|
setrlimit(resource, soft_limit, hard_limit)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
// SAFETY: the closure is self-contained and doesn't do any memory
|
||||||
|
// writes that would need to be propagated back to the parent process.
|
||||||
|
// also, the closure doesn't access stdin, stdout and stderr.
|
||||||
|
unsafe {
|
||||||
|
command.pre_exec(closure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(command, captured_stdout, captured_stderr, stdin_pty)
|
(command, captured_stdout, captured_stderr, stdin_pty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1660,17 +1682,6 @@ impl UCommand {
|
||||||
|
|
||||||
let child = command.spawn().unwrap();
|
let child = command.spawn().unwrap();
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
||||||
for &(resource, soft_limit, hard_limit) in &self.limits {
|
|
||||||
prlimit(
|
|
||||||
child.id() as i32,
|
|
||||||
resource,
|
|
||||||
Some((soft_limit, hard_limit)),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut child = UChild::from(self, child, captured_stdout, captured_stderr, stdin_pty);
|
let mut child = UChild::from(self, child, captured_stdout, captured_stderr, stdin_pty);
|
||||||
|
|
||||||
if let Some(input) = self.bytes_into_stdin.take() {
|
if let Some(input) = self.bytes_into_stdin.take() {
|
||||||
|
@ -3706,4 +3717,33 @@ mod tests {
|
||||||
);
|
);
|
||||||
std::assert_eq!(String::from_utf8_lossy(out.stderr()), "");
|
std::assert_eq!(String::from_utf8_lossy(out.stderr()), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[test]
|
||||||
|
fn test_application_of_process_resource_limits_unlimited_file_size() {
|
||||||
|
let ts = TestScenario::new("util");
|
||||||
|
ts.cmd("sh")
|
||||||
|
.args(&["-c", "ulimit -Sf; ulimit -Hf"])
|
||||||
|
.succeeds()
|
||||||
|
.no_stderr()
|
||||||
|
.stdout_is("unlimited\nunlimited\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[test]
|
||||||
|
fn test_application_of_process_resource_limits_limited_file_size() {
|
||||||
|
let unit_size_bytes = if cfg!(target_os = "macos") { 1024 } else { 512 };
|
||||||
|
|
||||||
|
let ts = TestScenario::new("util");
|
||||||
|
ts.cmd("sh")
|
||||||
|
.args(&["-c", "ulimit -Sf; ulimit -Hf"])
|
||||||
|
.limit(
|
||||||
|
rlimit::Resource::FSIZE,
|
||||||
|
8 * unit_size_bytes,
|
||||||
|
16 * unit_size_bytes,
|
||||||
|
)
|
||||||
|
.succeeds()
|
||||||
|
.no_stderr()
|
||||||
|
.stdout_is("8\n16\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue