#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
#![allow(non_snake_case)]
#![allow(clippy::unnecessary_cast)]
use std::sync::{Mutex, OnceLock};
#[cfg(unix)]
use libc::{
geteuid, getrlimit, rlim_t, rlimit, setrlimit, RLIMIT_AS, RLIMIT_CORE, RLIMIT_CPU,
RLIMIT_DATA, RLIMIT_FSIZE, RLIMIT_MEMLOCK, RLIMIT_NOFILE, RLIMIT_NPROC, RLIMIT_RSS,
RLIMIT_STACK, RLIM_INFINITY, RLIM_NLIMITS,
};
use crate::ported::utils::{zstrtol, zwarnnam};
use crate::ported::zsh_h::{module, options, OPT_ISSET};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum zlimtype {
ZLIMTYPE_MEMORY,
ZLIMTYPE_NUMBER,
ZLIMTYPE_TIME,
ZLIMTYPE_MICROSECONDS,
ZLIMTYPE_UNKNOWN,
}
#[derive(Debug, Clone)]
pub struct resinfo_T {
pub res: i32,
pub name: &'static str,
pub r#type: zlimtype,
pub unit: u32,
pub opt: char,
pub descr: &'static str,
}
#[cfg(unix)]
pub static known_resources: &[resinfo_T] = &[
resinfo_T {
res: RLIMIT_CPU as i32,
name: "cputime",
r#type: zlimtype::ZLIMTYPE_TIME,
unit: 1,
opt: 't',
descr: "cpu time (seconds)",
},
resinfo_T {
res: RLIMIT_FSIZE as i32,
name: "filesize",
r#type: zlimtype::ZLIMTYPE_MEMORY,
unit: 512,
opt: 'f',
descr: "file size (blocks)",
},
resinfo_T {
res: RLIMIT_DATA as i32,
name: "datasize",
r#type: zlimtype::ZLIMTYPE_MEMORY,
unit: 1024,
opt: 'd',
descr: "data seg size (kbytes)",
},
resinfo_T {
res: RLIMIT_STACK as i32,
name: "stacksize",
r#type: zlimtype::ZLIMTYPE_MEMORY,
unit: 1024,
opt: 's',
descr: "stack size (kbytes)",
},
resinfo_T {
res: RLIMIT_CORE as i32,
name: "coredumpsize",
r#type: zlimtype::ZLIMTYPE_MEMORY,
unit: 512,
opt: 'c',
descr: "core file size (blocks)",
},
resinfo_T {
res: RLIMIT_NOFILE as i32,
name: "descriptors",
r#type: zlimtype::ZLIMTYPE_NUMBER,
unit: 1,
opt: 'n',
descr: "file descriptors",
},
resinfo_T {
res: RLIMIT_AS as i32,
name: "addressspace",
r#type: zlimtype::ZLIMTYPE_MEMORY,
unit: 1024,
opt: 'v',
descr: "address space (kbytes)",
},
resinfo_T {
res: RLIMIT_RSS as i32,
name: "resident",
r#type: zlimtype::ZLIMTYPE_MEMORY,
unit: 1024,
opt: 'm',
descr: "resident set size (kbytes)",
},
resinfo_T {
res: RLIMIT_NPROC as i32,
name: "maxproc",
r#type: zlimtype::ZLIMTYPE_NUMBER,
unit: 1,
opt: 'u',
descr: "processes",
},
resinfo_T {
res: RLIMIT_MEMLOCK as i32,
name: "memorylocked",
r#type: zlimtype::ZLIMTYPE_MEMORY,
unit: 1024,
opt: 'l',
descr: "locked-in-memory size (kbytes)",
},
];
#[cfg(not(unix))]
pub static known_resources: &[resinfo_T] = &[];
static RESINFO: OnceLock<Mutex<Vec<resinfo_T>>> = OnceLock::new();
static CURRENT_LIMITS: OnceLock<Mutex<Vec<rlimit>>> = OnceLock::new();
static LIMITS: OnceLock<Mutex<Vec<rlimit>>> = OnceLock::new();
#[cfg(unix)]
fn nlimits() -> usize {
RLIM_NLIMITS as usize
}
#[cfg(not(unix))]
fn nlimits() -> usize {
0
}
#[cfg(unix)]
fn ensure_limits_initialized() {
let init = || {
let mut v: Vec<rlimit> = Vec::with_capacity(nlimits());
for i in 0..nlimits() as i32 {
let mut r = rlimit {
rlim_cur: 0,
rlim_max: 0,
};
unsafe {
getrlimit(i as _, &mut r);
}
v.push(r);
}
v
};
LIMITS.get_or_init(|| Mutex::new(init()));
CURRENT_LIMITS.get_or_init(|| Mutex::new(init()));
}
#[cfg(not(unix))]
fn ensure_limits_initialized() {}
pub(crate) fn set_resinfo() {
RESINFO.get_or_init(|| {
let mut v: Vec<resinfo_T> = Vec::with_capacity(nlimits());
for i in 0..nlimits() as i32 {
let entry = known_resources
.iter()
.find(|r| r.res == i)
.cloned()
.unwrap_or_else(|| resinfo_T {
res: -1,
name: leak_unknown_name(i),
r#type: zlimtype::ZLIMTYPE_UNKNOWN,
unit: 1,
opt: 'N',
descr: leak_unknown_name(i),
});
v.push(entry);
}
Mutex::new(v)
});
}
fn leak_unknown_name(i: i32) -> &'static str {
Box::leak(format!("UNKNOWN-{}", i).into_boxed_str())
}
pub(crate) fn free_resinfo() {
if let Some(lock) = RESINFO.get() {
if let Ok(mut v) = lock.lock() {
v.clear();
}
}
}
pub(crate) fn find_resource(c: char) -> i32 {
set_resinfo();
let lock = match RESINFO.get() {
Some(l) => l,
None => return -1,
};
let v = match lock.lock() {
Ok(v) => v,
Err(_) => return -1,
};
for (i, info) in v.iter().enumerate() {
if info.opt == c {
return i as i32;
}
}
-1
}
pub(crate) fn printrlim(val: rlim_t, unit: &str) {
print!("{}{}", val, unit);
}
#[cfg(unix)]
pub(crate) fn zstrtorlimt(s: &str, base: i32) -> (rlim_t, usize) {
if s == "unlimited" || s.starts_with("unlimited") {
return (RLIM_INFINITY, "unlimited".len());
}
let bytes = s.as_bytes();
let mut pos: usize = 0;
let mut base = base;
if base == 0 {
if pos < bytes.len() && bytes[pos] != b'0' {
base = 10;
} else {
pos += 1;
if pos < bytes.len() && (bytes[pos] == b'x' || bytes[pos] == b'X') {
base = 16;
pos += 1;
} else {
base = 8;
}
}
}
let mut ret: rlim_t = 0;
if base <= 10 {
while pos < bytes.len() {
let c = bytes[pos];
if !(c >= b'0' && c < b'0' + base as u8) {
break;
}
ret = ret * base as rlim_t + (c - b'0') as rlim_t;
pos += 1;
}
} else {
while pos < bytes.len() {
let c = bytes[pos];
let is_digit = c.is_ascii_digit();
let is_hex_lower = c >= b'a' && c < b'a' + (base as u8 - 10);
let is_hex_upper = c >= b'A' && c < b'A' + (base as u8 - 10);
if !(is_digit || is_hex_lower || is_hex_upper) {
break;
}
let digit = if is_digit {
(c - b'0') as rlim_t
} else {
((c & 0x1f) + 9) as rlim_t
};
ret = ret * base as rlim_t + digit;
pos += 1;
}
}
(ret, pos)
}
#[cfg(not(unix))]
pub(crate) fn zstrtorlimt(_s: &str, _base: i32) -> (u64, usize) {
(0, 0)
}
#[cfg(unix)]
pub(crate) fn showlimitvalue(lim: i32, val: rlim_t) {
set_resinfo();
let info = lookup_resinfo(lim);
if (lim as usize) < nlimits() {
if let Some(info) = info.as_ref() {
print!("{:<16}", info.name);
} else {
print!("{:<16}", lim);
}
} else {
print!("{:<16}", lim);
}
if val == RLIM_INFINITY {
println!("unlimited");
} else if (lim as usize) >= nlimits() {
printrlim(val, "\n");
} else if let Some(info) = info {
match info.r#type {
zlimtype::ZLIMTYPE_TIME => {
println!(
"{}:{:02}:{:02}",
val / 3600,
(val / 60) % 60,
val % 60
);
}
zlimtype::ZLIMTYPE_MICROSECONDS => printrlim(val, "us\n"),
zlimtype::ZLIMTYPE_NUMBER | zlimtype::ZLIMTYPE_UNKNOWN => printrlim(val, "\n"),
zlimtype::ZLIMTYPE_MEMORY => {
if val >= 1024 * 1024 {
printrlim(val / (1024 * 1024), "MB\n");
} else {
printrlim(val / 1024, "kB\n");
}
}
}
} else {
printrlim(val, "\n");
}
}
#[cfg(not(unix))]
pub(crate) fn showlimitvalue(_lim: i32, _val: u64) {}
#[cfg(unix)]
fn lookup_resinfo(lim: i32) -> Option<resinfo_T> {
if (lim as usize) >= nlimits() {
return None;
}
set_resinfo();
let lock = RESINFO.get()?;
let v = lock.lock().ok()?;
v.get(lim as usize).cloned()
}
#[cfg(unix)]
pub(crate) fn showlimits(nam: &str, hard: bool, lim: i32) -> i32 { ensure_limits_initialized();
if (lim as usize) >= nlimits() && lim != -1 {
let mut vals = rlimit {
rlim_cur: 0,
rlim_max: 0,
};
if unsafe { getrlimit(lim as _, &mut vals) } < 0 {
zwarnnam(nam, &format!("can't read limit: {}", std::io::Error::last_os_error()));
return 1;
}
showlimitvalue(lim, if hard { vals.rlim_max } else { vals.rlim_cur });
} else if lim != -1 {
let limits = match LIMITS.get() {
Some(l) => l,
None => return 1,
};
let v = limits.lock().unwrap();
let r = &v[lim as usize];
showlimitvalue(lim, if hard { r.rlim_max } else { r.rlim_cur });
} else {
let limits = match LIMITS.get() {
Some(l) => l,
None => return 1,
};
let v = limits.lock().unwrap();
for (rt, r) in v.iter().enumerate() {
showlimitvalue(rt as i32, if hard { r.rlim_max } else { r.rlim_cur });
}
}
0
}
#[cfg(not(unix))]
pub(crate) fn showlimits(_nam: &str, _hard: bool, _lim: i32) -> i32 {
0
}
#[cfg(unix)]
pub(crate) fn printulimit(nam: &str, lim: i32, hard: bool, head: bool) -> i32 { ensure_limits_initialized();
let limit: rlim_t = if (lim as usize) >= nlimits() {
let mut vals = rlimit {
rlim_cur: 0,
rlim_max: 0,
};
if unsafe { getrlimit(lim as _, &mut vals) } < 0 {
zwarnnam(nam, &format!("can't read limit: {}", std::io::Error::last_os_error()));
return 1;
}
if hard { vals.rlim_max } else { vals.rlim_cur }
} else {
let limits = match LIMITS.get() {
Some(l) => l,
None => return 1,
};
let v = limits.lock().unwrap();
let r = &v[lim as usize];
if hard { r.rlim_max } else { r.rlim_cur }
};
if head {
if (lim as usize) < nlimits() {
if let Some(info) = lookup_resinfo(lim) {
if info.opt == 'N' {
print!("-N {:>2}: {:<29}", lim, info.descr);
} else {
print!("-{}: {:<32}", info.opt, info.descr);
}
} else {
print!("-N {:>2}: {:<29}", lim, "");
}
} else {
print!("-N {:>2}: {:<29}", lim, "");
}
}
if limit == RLIM_INFINITY {
println!("unlimited");
} else if (lim as usize) < nlimits() {
let info = lookup_resinfo(lim);
let unit = info.map(|i| i.unit).unwrap_or(1) as rlim_t;
printrlim(limit / unit, "\n");
} else {
printrlim(limit, "\n");
}
0
}
#[cfg(not(unix))]
pub(crate) fn printulimit(_nam: &str, _lim: i32, _hard: bool, _head: bool) -> i32 {
0
}
#[cfg(unix)]
pub(crate) fn do_limit( nam: &str,
lim: i32,
val: rlim_t,
hard: bool,
soft: bool,
set: bool,
) -> i32 {
ensure_limits_initialized();
if (lim as usize) >= nlimits() {
let mut vals = rlimit {
rlim_cur: 0,
rlim_max: 0,
};
if unsafe { getrlimit(lim as _, &mut vals) } < 0 {
zwarnnam(nam, &format!("can't read limit: {}", std::io::Error::last_os_error()));
return 1;
}
if hard {
if val > vals.rlim_max && unsafe { geteuid() } != 0 {
zwarnnam(nam, "can't raise hard limits");
return 1;
}
vals.rlim_max = val;
if val < vals.rlim_cur {
vals.rlim_cur = val;
}
}
if soft || !hard {
if val > vals.rlim_max {
zwarnnam(nam, "limit exceeds hard limit");
return 1;
}
vals.rlim_cur = val;
}
if !set {
zwarnnam(nam, &format!("warning: unrecognised limit {}, use -s to set", lim));
return 1;
} else if unsafe { setrlimit(lim as _, &vals) } < 0 {
zwarnnam(nam, &format!("setrlimit failed: {}", std::io::Error::last_os_error()));
return 1;
}
} else {
let cur_max = CURRENT_LIMITS
.get()
.and_then(|l| l.lock().ok())
.map(|v| v[lim as usize].rlim_max)
.unwrap_or(0);
if hard {
if val > cur_max && unsafe { geteuid() } != 0 {
zwarnnam(nam, "can't raise hard limits");
return 1;
}
if let Some(lock) = LIMITS.get() {
let mut v = lock.lock().unwrap();
v[lim as usize].rlim_max = val;
if val < v[lim as usize].rlim_cur {
v[lim as usize].rlim_cur = val;
}
}
}
if soft || !hard {
let cur_lim_max = LIMITS
.get()
.and_then(|l| l.lock().ok())
.map(|v| v[lim as usize].rlim_max)
.unwrap_or(0);
if val > cur_lim_max {
if nam.starts_with('u') {
if val > cur_max && unsafe { geteuid() } != 0 {
zwarnnam(nam, "value exceeds hard limit");
return 1;
}
if let Some(lock) = LIMITS.get() {
let mut v = lock.lock().unwrap();
v[lim as usize].rlim_max = val;
v[lim as usize].rlim_cur = val;
}
} else {
zwarnnam(nam, "limit exceeds hard limit");
return 1;
}
} else if let Some(lock) = LIMITS.get() {
let mut v = lock.lock().unwrap();
v[lim as usize].rlim_cur = val;
}
if set && zsetlimit(lim, nam) != 0 {
return 1;
}
}
}
0
}
#[cfg(not(unix))]
pub(crate) fn do_limit(
_nam: &str,
_lim: i32,
_val: u64,
_hard: bool,
_soft: bool,
_set: bool,
) -> i32 {
0
}
#[cfg(unix)]
fn zsetlimit(limnum: i32, nam: &str) -> i32 {
ensure_limits_initialized();
let limits_lock = match LIMITS.get() {
Some(l) => l,
None => return 0,
};
let cur_lock = match CURRENT_LIMITS.get() {
Some(l) => l,
None => return 0,
};
let limits = limits_lock.lock().unwrap();
let limits_v = limits[limnum as usize];
drop(limits);
let cur = cur_lock.lock().unwrap();
let cur_v = cur[limnum as usize];
drop(cur);
if limits_v.rlim_max != cur_v.rlim_max || limits_v.rlim_cur != cur_v.rlim_cur {
if unsafe { setrlimit(limnum as _, &limits_v) } < 0 {
zwarnnam(nam, &format!("setrlimit failed: {}", std::io::Error::last_os_error()));
let mut limits = limits_lock.lock().unwrap();
limits[limnum as usize] = cur_v;
return 1;
}
let mut cur = cur_lock.lock().unwrap();
cur[limnum as usize] = limits_v;
}
0
}
#[cfg(not(unix))]
fn zsetlimit(_limnum: i32, _nam: &str) -> i32 {
0
}
#[cfg(unix)]
fn setlimits(nam: &str) -> i32 {
let mut ret = 0;
for i in 0..nlimits() as i32 {
if zsetlimit(i, nam) != 0 {
ret += 1;
}
}
ret
}
#[cfg(not(unix))]
fn setlimits(_nam: &str) -> i32 {
0
}
#[cfg(unix)]
pub(crate) fn bin_limit(nam: &str, argv: &[String], ops: &options, _func: i32) -> i32 { let hard: bool;
let mut limnum: i32;
let mut lim: i32;
let mut val: rlim_t;
let mut ret: i32 = 0;
ensure_limits_initialized();
set_resinfo();
hard = OPT_ISSET(ops, b'h'); if OPT_ISSET(ops, b's') && argv.is_empty() {
return setlimits(""); }
if argv.is_empty() {
return showlimits(nam, hard, -1); }
let mut argi = argv.iter(); while let Some(s_owned) = argi.next() {
let s: &str = s_owned.as_str(); let sb = s.as_bytes();
if !sb.is_empty() && sb[0].is_ascii_digit() { lim = zstrtol(s, 10).0 as i32;
} else {
lim = -1;
limnum = 0;
let resinfo_lock = RESINFO.get().unwrap();
let v = resinfo_lock.lock().unwrap();
while (limnum as usize) < v.len() {
if v[limnum as usize].name.starts_with(s) { if lim != -1 {
lim = -2;
} else {
lim = limnum;
}
}
limnum += 1;
}
}
if lim < 0 {
zwarnnam(
nam,
&if lim == -2 {
format!("ambiguous resource specification: {}", s)
} else {
format!("no such resource: {}", s)
},
);
return 1;
}
let s_val: &str = match argi.next() {
None => return showlimits(nam, hard, lim), Some(t) => t.as_str(),
};
if (lim as usize) >= RLIM_NLIMITS as usize { let (v, consumed) = zstrtorlimt(s_val, 10); if consumed != s_val.len() { zwarnnam(
nam,
&format!("unknown scaling factor: {}", &s_val[consumed..]),
);
return 1;
}
val = v;
} else {
let resinfo_lock = RESINFO.get().unwrap();
let info_type = resinfo_lock.lock().unwrap()[lim as usize].r#type;
match info_type {
zlimtype::ZLIMTYPE_TIME => {
let (mut v, consumed) = zstrtorlimt(s_val, 10); let rest = &s_val[consumed..];
let rb = rest.as_bytes();
if !rest.is_empty() { if (rb[0] == b'h' || rb[0] == b'H') && rb.len() == 1 { v = v.saturating_mul(3600);
} else if (rb[0] == b'm' || rb[0] == b'M') && rb.len() == 1 { v = v.saturating_mul(60);
} else if rb[0] == b':' { let (more, _) = zstrtorlimt(&rest[1..], 10);
v = v.saturating_mul(60).saturating_add(more); } else { zwarnnam(nam, &format!("unknown scaling factor: {}", rest));
return 1;
}
}
val = v;
}
zlimtype::ZLIMTYPE_NUMBER
| zlimtype::ZLIMTYPE_UNKNOWN
| zlimtype::ZLIMTYPE_MICROSECONDS => {
let t = s_val; let (v, consumed) = zstrtorlimt(t, 10); if consumed == 0 { zwarnnam(nam, "limit must be a number"); return 1;
}
val = v;
}
zlimtype::ZLIMTYPE_MEMORY => {
let (mut v, consumed) = zstrtorlimt(s_val, 10); let rest = &s_val[consumed..];
let rb = rest.as_bytes();
if rest.is_empty() || ((rb[0] == b'k' || rb[0] == b'K') && rb.len() == 1) { if v != RLIM_INFINITY { v = v.saturating_mul(1024); }
} else if (rb[0] == b'M' || rb[0] == b'm') && rb.len() == 1 { v = v.saturating_mul(1024 * 1024); } else if (rb[0] == b'G' || rb[0] == b'g') && rb.len() == 1 { v = v.saturating_mul(1024 * 1024 * 1024); } else { zwarnnam(nam, &format!("unknown scaling factor: {}", rest));
return 1;
}
val = v;
}
}
}
if do_limit(nam, lim, val, hard, !hard, OPT_ISSET(ops, b's')) != 0 {
ret += 1; }
}
ret }
#[cfg(not(unix))]
pub(crate) fn bin_limit(_nam: &str, _argv: &[String], _ops: &options, _func: i32) -> i32 {
0
}
#[cfg(unix)]
pub(crate) fn do_unlimit( nam: &str,
lim: i32,
hard: bool,
soft: bool,
set: bool,
euid: u32,
) -> i32 {
ensure_limits_initialized();
if (lim as usize) >= nlimits() {
let mut vals = rlimit {
rlim_cur: 0,
rlim_max: 0,
};
if unsafe { getrlimit(lim as _, &mut vals) } < 0 {
zwarnnam(nam, &format!("can't read limit: {}", std::io::Error::last_os_error()));
return 1;
}
if hard {
if euid != 0 && vals.rlim_max != RLIM_INFINITY {
zwarnnam(nam, "can't remove hard limits");
return 1;
}
vals.rlim_max = RLIM_INFINITY;
}
if !hard || soft {
vals.rlim_cur = vals.rlim_max;
}
if !set {
zwarnnam(nam, &format!("warning: unrecognised limit {}, use -s to set", lim));
return 1;
} else if unsafe { setrlimit(lim as _, &vals) } < 0 {
zwarnnam(nam, &format!("setrlimit failed: {}", std::io::Error::last_os_error()));
return 1;
}
} else {
if hard {
let cur_max = CURRENT_LIMITS
.get()
.and_then(|l| l.lock().ok())
.map(|v| v[lim as usize].rlim_max)
.unwrap_or(0);
if euid != 0 && cur_max != RLIM_INFINITY {
zwarnnam(nam, "can't remove hard limits");
return 1;
} else if let Some(lock) = LIMITS.get() {
let mut v = lock.lock().unwrap();
v[lim as usize].rlim_max = RLIM_INFINITY;
}
}
if !hard || soft {
if let Some(lock) = LIMITS.get() {
let mut v = lock.lock().unwrap();
v[lim as usize].rlim_cur = v[lim as usize].rlim_max;
}
}
if set && zsetlimit(lim, nam) != 0 {
return 1;
}
}
0
}
#[cfg(not(unix))]
pub(crate) fn do_unlimit(
_nam: &str,
_lim: i32,
_hard: bool,
_soft: bool,
_set: bool,
_euid: u32,
) -> i32 {
0
}
#[cfg(unix)]
pub(crate) fn bin_unlimit(nam: &str, argv: &[String], ops: &options, _func: i32) -> i32 { let hard: bool;
let mut limnum: i32;
let mut lim: i32;
let mut ret: i32 = 0;
let euid: libc::uid_t = unsafe { geteuid() };
ensure_limits_initialized();
set_resinfo();
hard = OPT_ISSET(ops, b'h'); if argv.is_empty() {
limnum = 0;
while limnum != RLIM_NLIMITS as i32 {
if hard { let cur_max = CURRENT_LIMITS
.get()
.and_then(|l| l.lock().ok())
.map(|v| v[limnum as usize].rlim_max)
.unwrap_or(0);
if euid != 0 && cur_max != RLIM_INFINITY { ret += 1; } else if let Some(lock) = LIMITS.get() { let mut v = lock.lock().unwrap();
v[limnum as usize].rlim_max = RLIM_INFINITY;
}
} else if let Some(lock) = LIMITS.get() { let mut v = lock.lock().unwrap();
v[limnum as usize].rlim_cur = v[limnum as usize].rlim_max;
}
limnum += 1;
}
if OPT_ISSET(ops, b's') { ret += setlimits(nam); }
if ret != 0 { zwarnnam(nam, "can't remove hard limits"); }
} else {
let mut argi = argv.iter();
while let Some(arg) = argi.next() {
let s: &str = arg.as_str();
let sb = s.as_bytes();
if !sb.is_empty() && sb[0].is_ascii_digit() { lim = zstrtol(s, 10).0 as i32; } else {
lim = -1; limnum = 0;
let resinfo_lock = RESINFO.get().unwrap();
let v = resinfo_lock.lock().unwrap();
while (limnum as usize) < v.len() {
if v[limnum as usize].name.starts_with(s) { if lim != -1 {
lim = -2; } else {
lim = limnum; }
}
limnum += 1;
}
}
if lim < 0 {
zwarnnam(
nam,
&if lim == -2 {
format!("ambiguous resource specification: {}", s)
} else {
format!("no such resource: {}", s)
},
);
return 1;
} else if do_unlimit(nam, lim, hard, !hard, OPT_ISSET(ops, b's'), euid) != 0 {
ret += 1; }
}
}
ret }
#[cfg(not(unix))]
pub(crate) fn bin_unlimit(_nam: &str, _argv: &[String], _ops: &options, _func: i32) -> i32 {
0
}
#[cfg(unix)]
pub(crate) fn bin_ulimit(
name: &str,
argv: &[String],
_ops: &options,
_func: i32,
) -> i32 {
let mut res: i32;
let mut resmask: u64 = 0;
let mut hard: bool = false;
let mut soft: bool = false;
let mut nres: i32 = 0;
let mut all: bool = false;
let mut ret: i32 = 0;
ensure_limits_initialized();
set_resinfo();
let mut argi: usize = 0;
loop {
let options: Option<&String> = argv.get(argi);
if let Some(o) = options {
if o == "-" {
zwarnnam(name, "missing option letter"); return 1;
}
}
res = -1; if options.map(|o| o.starts_with('-') && o.len() > 1).unwrap_or(false) {
argi += 1; let opt_str = options.unwrap().clone();
let opt_bytes = opt_str.as_bytes();
let mut p: usize = 1; while p < opt_bytes.len() {
let mut c = opt_bytes[p];
res = -1; let mut continue_outer = false;
match c {
b'H' => { hard = true;
p += 1;
continue_outer = true;
}
b'S' => { soft = true;
p += 1;
continue_outer = true;
}
b'N' => { let number: String = if p + 1 < opt_bytes.len() {
let n = std::str::from_utf8(&opt_bytes[p + 1..])
.unwrap_or("")
.to_string();
n
} else if argi < argv.len() {
let n = argv[argi].clone();
argi += 1;
n
} else {
zwarnnam(name, "number required after -N"); return 1;
};
let nb = number.as_bytes();
let mut consumed = 0;
let mut acc: i64 = 0;
while consumed < nb.len() && nb[consumed].is_ascii_digit() {
acc = acc.saturating_mul(10).saturating_add((nb[consumed] - b'0') as i64);
consumed += 1;
}
if consumed != nb.len() { zwarnnam(name, &format!("invalid number: {}", number));
return 1;
}
res = acc as i32;
p = opt_bytes.len();
}
b'a' => { if resmask != 0 {
zwarnnam(name, "no limits allowed with -a"); return 1;
}
all = true;
resmask = (1u64 << RLIM_NLIMITS as i32) - 1; nres = RLIM_NLIMITS as i32; p += 1;
continue_outer = true;
}
_ => { res = find_resource(c as char); if res < 0 {
zwarnnam(name, &format!("bad option: -{}", c as char));
return 1;
}
}
}
if continue_outer {
continue;
}
if p + 1 < opt_bytes.len() { resmask |= 1u64 << res; nres += 1; }
if all && res != -1 { zwarnnam(name, "no limits allowed with -a"); return 1;
}
p += 1;
if c == b'N' {
}
let _ = c; c = 0; let _ = c;
}
}
let next_is_dash = argv
.get(argi)
.map(|a| a.starts_with('-'))
.unwrap_or(true);
if next_is_dash {
if res < 0 { if argi < argv.len() || nres != 0 { if argi >= argv.len() {
break;
}
continue; } else {
res = RLIMIT_FSIZE as i32; }
}
resmask |= 1u64 << res; nres += 1; if argi >= argv.len() {
break; }
continue; }
if all { zwarnnam(name, "no arguments allowed after -a"); return 1;
}
if res < 0 { res = RLIMIT_FSIZE as i32; }
let arg = argv[argi].clone();
argi += 1; if arg != "unlimited" {
let limit: rlim_t;
if arg == "hard" { let mut vals = rlimit { rlim_cur: 0, rlim_max: 0 }; if unsafe { getrlimit(res as _, &mut vals) } < 0 { zwarnnam(
name,
&format!("can't read limit: {}", std::io::Error::last_os_error()),
);
return 1;
}
limit = vals.rlim_max; } else {
let (mut v, consumed) = zstrtorlimt(&arg, 10); if consumed != arg.len() { zwarnnam(name, &format!("invalid number: {}", arg)); return 1;
}
if (res as usize) < RLIM_NLIMITS as usize { let resinfo_lock = RESINFO.get().unwrap();
let unit = resinfo_lock.lock().unwrap()[res as usize].unit as rlim_t;
v = v.saturating_mul(unit); }
limit = v;
}
if do_limit(name, res, limit, hard, soft, true) != 0 { ret += 1; }
} else { if do_unlimit(name, res, hard, soft, true, unsafe { geteuid() }) != 0 { ret += 1; }
}
if argi >= argv.len() {
break;
}
}
res = 0;
while resmask != 0 {
if (resmask & 1) != 0 && printulimit(name, res, hard, nres > 1) != 0 { ret += 1; }
res += 1;
resmask >>= 1;
}
ret }
#[cfg(not(unix))]
pub(crate) fn bin_ulimit(
_name: &str,
_argv: &[String],
_ops: &options,
_func: i32,
) -> i32 {
0
}
use crate::ported::zsh_h::features as features_t;
static MODULE_FEATURES: OnceLock<Mutex<features_t>> = OnceLock::new();
fn module_features() -> &'static Mutex<features_t> {
MODULE_FEATURES.get_or_init(|| {
Mutex::new(features_t {
bn_list: None, bn_size: 3, cd_list: None, cd_size: 0,
mf_list: None, mf_size: 0,
pd_list: None, pd_size: 0,
n_abstract: 0, })
})
}
#[allow(unused_variables)]
pub fn setup_(m: *const module) -> i32 {
0 }
pub fn features_(m: *const module, features: &mut Vec<String>) -> i32 {
*features = featuresarray(m, module_features()); 0 }
pub fn enables_(m: *const module, enables: &mut Option<Vec<i32>>) -> i32 {
handlefeatures(m, module_features(), enables) }
#[allow(unused_variables)]
pub fn boot_(m: *const module) -> i32 {
set_resinfo(); 0 }
pub fn cleanup_(m: *const module) -> i32 {
free_resinfo(); setfeatureenables(m, module_features(), None) }
#[allow(unused_variables)]
pub fn finish_(m: *const module) -> i32 {
0 }
fn featuresarray(_m: *const module, _f: &Mutex<features_t>) -> Vec<String> {
vec!["b:limit".to_string(), "b:ulimit".to_string(), "b:unlimit".to_string()]
}
fn handlefeatures(m: *const module, f: &Mutex<features_t>, enables: &mut Option<Vec<i32>>) -> i32 {
if enables.is_none() {
*enables = Some(getfeatureenables(m, f));
} else if let Some(e) = enables.as_ref() {
return setfeatureenables(m, f, Some(e));
}
0
}
fn getfeatureenables(_m: *const module, f: &Mutex<features_t>) -> Vec<i32> {
let g = f.lock().unwrap();
let total = g.bn_size + g.cd_size + g.mf_size + g.pd_size + g.n_abstract;
vec![0; total as usize]
}
fn setfeatureenables(_m: *const module, _f: &Mutex<features_t>, _e: Option<&Vec<i32>>) -> i32 {
0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(unix)]
fn test_zstrtorlimt_unlimited() {
let (v, consumed) = zstrtorlimt("unlimited", 10);
assert_eq!(v, RLIM_INFINITY);
assert_eq!(consumed, "unlimited".len());
}
#[test]
#[cfg(unix)]
fn test_zstrtorlimt_decimal() {
let (v, consumed) = zstrtorlimt("1234", 10);
assert_eq!(v, 1234);
assert_eq!(consumed, 4);
}
#[test]
#[cfg(unix)]
fn test_zstrtorlimt_hex() {
let (v, consumed) = zstrtorlimt("0xff", 0);
assert_eq!(v, 255);
assert_eq!(consumed, 4);
}
#[test]
#[cfg(unix)]
fn test_find_resource() {
set_resinfo();
assert!(find_resource('t') >= 0);
assert!(find_resource('f') >= 0);
assert_eq!(find_resource('z'), -1);
}
#[test]
#[cfg(unix)]
fn test_set_resinfo_populates_table() {
set_resinfo();
let lock = RESINFO.get().unwrap();
let v = lock.lock().unwrap();
assert_eq!(v.len(), nlimits());
}
}