use std::env;
use std::ffi::OsStr;
use nix::unistd::{sysconf, SysconfVar};
use once_cell::sync::Lazy;
use crate::constants::POINTER_SIZE_CONSERVATIVE;
const UPPER_BOUND_COMMAND_LINE_LENGTH: i64 = 16 * 1024 * 1024;
static PAGE_SIZE: Lazy<i64> = Lazy::new(|| {
sysconf(SysconfVar::PAGE_SIZE)
.ok()
.flatten()
.map(|page_size| page_size as i64)
.filter(|&s| s >= 4096)
.unwrap_or(4096)
});
pub static MAX_SINGLE_ARGUMENT_LENGTH: Lazy<i64> = Lazy::new(|| 32 * *PAGE_SIZE - 1);
fn environment_variable_size<O: AsRef<OsStr>>(key: O, value: O) -> i64 {
POINTER_SIZE_CONSERVATIVE + key.as_ref().len() as i64 + 1 + value.as_ref().len() as i64 + 1 }
fn size_of_environment() -> i64 {
env::vars_os()
.map(|(key, value)| environment_variable_size(&key, &value))
.sum()
}
pub(crate) fn arg_size<O: AsRef<OsStr>>(arg: O) -> i64 {
POINTER_SIZE_CONSERVATIVE + arg.as_ref().len() as i64 + 1 }
pub(crate) fn available_argument_length<O: AsRef<OsStr>>(
fixed_args: impl Iterator<Item = O>,
) -> Option<i64> {
let mut arg_max = sysconf(SysconfVar::ARG_MAX).ok().flatten()? as i64;
if arg_max < 0 {
arg_max = UPPER_BOUND_COMMAND_LINE_LENGTH;
}
if cfg!(all(target_os = "illumos", target_pointer_width = "64")) {
arg_max /= 2;
}
arg_max -= size_of_environment();
arg_max -= POINTER_SIZE_CONSERVATIVE;
arg_max -= fixed_args.map(|a| arg_size(a.as_ref())).sum::<i64>();
arg_max -= POINTER_SIZE_CONSERVATIVE;
arg_max -= *PAGE_SIZE;
arg_max -= 2048;
if arg_max < 0 {
arg_max = 0;
} else if arg_max > UPPER_BOUND_COMMAND_LINE_LENGTH {
arg_max = UPPER_BOUND_COMMAND_LINE_LENGTH;
}
Some(arg_max)
}
pub(crate) fn max_single_argument_length() -> i64 {
*MAX_SINGLE_ARGUMENT_LENGTH
}
#[cfg(test)]
mod tests {
use crate::experimental_limit::experimental_arg_limit;
#[test]
fn available_argument_length_is_smaller_than_experimental_limit() {
use super::available_argument_length;
use std::ffi::OsStr;
let experimental_limit = experimental_arg_limit();
println!("Experimental limit: {}", experimental_limit);
let arg_size = 8 + 3 + 1;
let experimental_size_limit = experimental_limit * arg_size + 8 + 1 + "echo".len() as i64;
assert!(
available_argument_length([OsStr::new("echo")].iter()).unwrap_or(0)
<= experimental_size_limit
);
}
}