Port builtin ulimit

Closes #10121
This commit is contained in:
Thomas Queiroz 2023-11-26 07:47:30 -03:00 committed by Johannes Altmanninger
parent 31ecc5e0f9
commit a64324421f
9 changed files with 646 additions and 474 deletions

View file

@ -101,7 +101,6 @@ endif()
set(FISH_BUILTIN_SRCS set(FISH_BUILTIN_SRCS
src/builtins/bind.cpp src/builtins/bind.cpp
src/builtins/commandline.cpp src/builtins/commandline.cpp
src/builtins/ulimit.cpp
) )
# List of other sources. # List of other sources.
set(FISH_SRCS set(FISH_SRCS

View file

@ -51,7 +51,7 @@ lazy_static = "1.4.0"
libc = "0.2.137" libc = "0.2.137"
lru = "0.10.0" lru = "0.10.0"
moveit = "0.5.1" moveit = "0.5.1"
nix = { version = "0.25.0", default-features = false, features = ["inotify"] } nix = { version = "0.25.0", default-features = false, features = ["inotify", "resource"] }
num-traits = "0.2.15" num-traits = "0.2.15"
# to make integer->enum conversion easier # to make integer->enum conversion easier
num-derive = "0.3.3" num-derive = "0.3.3"

View file

@ -1,16 +1,485 @@
use std::cmp::Ordering;
use libc::{c_uint, rlim_t, RLIM_INFINITY};
use nix::errno::Errno;
use once_cell::sync::Lazy;
use crate::compat::*;
use crate::fallback::{fish_wcswidth, wcscasecmp};
use crate::wutil::perror;
use super::prelude::*; use super::prelude::*;
/// Calls getrlimit.
fn getrlimit(resource: c_uint) -> Option<(rlim_t, rlim_t)> {
let resource: i32 = resource.try_into().unwrap();
// Resource is #[repr(i32)] so this is ok
nix::sys::resource::getrlimit(unsafe { std::mem::transmute(resource) })
.map_err(|_| {
perror("getrlimit");
})
.ok()
}
fn setrlimit(resource: c_uint, rlim_cur: rlim_t, rlim_max: rlim_t) -> Result<(), Errno> {
let resource: i32 = resource.try_into().unwrap();
nix::sys::resource::setrlimit(
unsafe { std::mem::transmute(resource) }, // Resource is #[repr(i32)] so this is ok
rlim_cur,
rlim_max,
)
}
/// Print the value of the specified resource limit.
fn print(resource: c_uint, hard: bool, streams: &mut IoStreams) {
let Some(l) = get(resource, hard) else {
streams.out.append(wgettext!("error\n"));
return;
};
if l == RLIM_INFINITY {
streams.out.append(wgettext!("unlimited\n"));
} else {
streams
.out
.append(wgettext_fmt!("%lu\n", l / get_multiplier(resource)));
}
}
/// Print values of all resource limits.
fn print_all(hard: bool, streams: &mut IoStreams) {
let mut w = 0;
for resource in RESOURCE_ARR.iter() {
w = w.max(fish_wcswidth(resource.desc));
}
for resource in RESOURCE_ARR.iter() {
let Some((rlim_cur, rlim_max)) = getrlimit(resource.resource) else {
continue;
};
let l = if hard { rlim_max } else { rlim_cur };
let unit = if resource.resource == RLIMIT_CPU() as c_uint {
"(seconds, "
} else if get_multiplier(resource.resource) == 1 {
"("
} else {
"(kB, "
};
streams.out.append(wgettext_fmt!(
"%-*ls %10ls-%lc) ",
w,
resource.desc,
unit,
resource.switch_char,
));
if l == RLIM_INFINITY {
streams.out.append(wgettext!("unlimited\n"));
} else {
streams.out.append(wgettext_fmt!(
"%lu\n",
l / get_multiplier(resource.resource)
));
}
}
}
/// Returns the description for the specified resource limit.
fn get_desc(what: c_uint) -> &'static wstr {
for resource in RESOURCE_ARR.iter() {
if resource.resource == what {
return resource.desc;
}
}
unreachable!()
}
/// Set the new value of the specified resource limit. This function does _not_ multiply the limit
/// value by the multiplier constant used by the commandline ulimit.
fn set_limit(
resource: c_uint,
hard: bool,
soft: bool,
value: rlim_t,
streams: &mut IoStreams,
) -> Option<c_int> {
let Some((mut rlim_cur, mut rlim_max)) = getrlimit(resource) else {
return STATUS_CMD_ERROR;
};
if hard {
rlim_max = value;
}
if soft {
rlim_cur = value;
// Do not attempt to set the soft limit higher than the hard limit.
if (value > rlim_max || value == RLIM_INFINITY) && rlim_max != RLIM_INFINITY {
rlim_cur = rlim_max;
}
}
if let Err(errno) = setrlimit(resource, rlim_cur, rlim_max) {
if errno == Errno::EPERM {
streams.err.append(wgettext_fmt!(
"ulimit: Permission denied when changing resource of type '%ls'\n",
get_desc(resource)
));
} else {
builtin_wperror(L!("ulimit"), streams);
}
STATUS_CMD_ERROR
} else {
STATUS_CMD_OK
}
}
/// Get the implicit multiplication factor for the specified resource limit.
fn get_multiplier(what: c_uint) -> rlim_t {
for resource in RESOURCE_ARR.iter() {
if resource.resource == what {
return resource.multiplier as rlim_t;
}
}
unreachable!()
}
fn get(resource: c_uint, hard: bool) -> Option<rlim_t> {
let (rlim_cur, rlim_max) = getrlimit(resource)?;
Some(if hard { rlim_max } else { rlim_cur })
}
#[derive(Debug, Clone, Copy)]
struct Options {
what: c_int,
report_all: bool,
hard: bool,
soft: bool,
}
impl Default for Options {
fn default() -> Self {
Options {
what: RLIMIT_FSIZE(),
report_all: false,
hard: false,
soft: false,
}
}
}
pub fn ulimit(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Option<c_int> { pub fn ulimit(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Option<c_int> {
run_builtin_ffi(crate::ffi::builtin_ulimit, parser, streams, args) let cmd = args[0];
const SHORT_OPTS: &wstr = L!(":HSabcdefilmnqrstuvwyKPTh");
const LONG_OPTS: &[woption] = &[
wopt(L!("all"), woption_argument_t::no_argument, 'a'),
wopt(L!("hard"), woption_argument_t::no_argument, 'H'),
wopt(L!("soft"), woption_argument_t::no_argument, 'S'),
wopt(L!("socket-buffers"), woption_argument_t::no_argument, 'b'),
wopt(L!("core-size"), woption_argument_t::no_argument, 'c'),
wopt(L!("data-size"), woption_argument_t::no_argument, 'd'),
wopt(L!("nice"), woption_argument_t::no_argument, 'e'),
wopt(L!("file-size"), woption_argument_t::no_argument, 'f'),
wopt(L!("pending-signals"), woption_argument_t::no_argument, 'i'),
wopt(L!("lock-size"), woption_argument_t::no_argument, 'l'),
wopt(
L!("resident-set-size"),
woption_argument_t::no_argument,
'm',
),
wopt(
L!("file-descriptor-count"),
woption_argument_t::no_argument,
'n',
),
wopt(L!("queue-size"), woption_argument_t::no_argument, 'q'),
wopt(
L!("realtime-priority"),
woption_argument_t::no_argument,
'r',
),
wopt(L!("stack-size"), woption_argument_t::no_argument, 's'),
wopt(L!("cpu-time"), woption_argument_t::no_argument, 't'),
wopt(L!("process-count"), woption_argument_t::no_argument, 'u'),
wopt(
L!("virtual-memory-size"),
woption_argument_t::no_argument,
'v',
),
wopt(L!("swap-size"), woption_argument_t::no_argument, 'w'),
wopt(L!("realtime-maxtime"), woption_argument_t::no_argument, 'y'),
wopt(L!("kernel-queues"), woption_argument_t::no_argument, 'K'),
wopt(L!("ptys"), woption_argument_t::no_argument, 'P'),
wopt(L!("threads"), woption_argument_t::no_argument, 'T'),
wopt(L!("help"), woption_argument_t::no_argument, 'h'),
];
let mut opts = Options::default();
let mut w = wgetopter_t::new(SHORT_OPTS, LONG_OPTS, args);
while let Some(c) = w.wgetopt_long() {
match c {
'a' => opts.report_all = true,
'H' => opts.hard = true,
'S' => opts.soft = true,
'b' => opts.what = RLIMIT_SBSIZE(),
'c' => opts.what = RLIMIT_CORE(),
'd' => opts.what = RLIMIT_DATA(),
'e' => opts.what = RLIMIT_NICE(),
'f' => opts.what = RLIMIT_FSIZE(),
'i' => opts.what = RLIMIT_SIGPENDING(),
'l' => opts.what = RLIMIT_MEMLOCK(),
'm' => opts.what = RLIMIT_RSS(),
'n' => opts.what = RLIMIT_NOFILE(),
'q' => opts.what = RLIMIT_MSGQUEUE(),
'r' => opts.what = RLIMIT_RTPRIO(),
's' => opts.what = RLIMIT_STACK(),
't' => opts.what = RLIMIT_CPU(),
'u' => opts.what = RLIMIT_NPROC(),
'v' => opts.what = RLIMIT_AS(),
'w' => opts.what = RLIMIT_SWAP(),
'y' => opts.what = RLIMIT_RTTIME(),
'K' => opts.what = RLIMIT_KQUEUES(),
'P' => opts.what = RLIMIT_NPTS(),
'T' => opts.what = RLIMIT_NTHR(),
'h' => {
builtin_print_help(parser, streams, cmd);
return STATUS_CMD_OK;
}
':' => {
builtin_missing_argument(parser, streams, cmd, w.argv[w.woptind - 1], true);
return STATUS_INVALID_ARGS;
}
'?' => {
builtin_unknown_option(parser, streams, cmd, w.argv[w.woptind - 1], true);
return STATUS_INVALID_ARGS;
}
_ => {
panic!("unexpected retval from wgetopt_long");
}
}
}
if opts.report_all {
print_all(opts.hard, streams);
}
if opts.what == -1 {
streams.err.append(wgettext_fmt!(
"%ls: Resource limit not available on this operating system\n",
cmd
));
builtin_print_error_trailer(parser, streams.err, cmd);
return STATUS_INVALID_ARGS;
}
let what: c_uint = opts.what.try_into().unwrap();
let argc = w.argv.len();
let arg_count = argc - w.woptind;
if arg_count == 0 {
print(what, opts.hard, streams);
return STATUS_CMD_OK;
} else if arg_count != 1 {
streams
.err
.append(wgettext_fmt!(BUILTIN_ERR_TOO_MANY_ARGUMENTS, cmd));
builtin_print_error_trailer(parser, streams.err, cmd);
return STATUS_INVALID_ARGS;
}
let mut hard = opts.hard;
let mut soft = opts.soft;
if !hard && !soft {
// Set both hard and soft limits if neither was specified.
hard = true;
soft = true;
}
let new_limit: rlim_t = if w.woptind == argc {
streams.err.append(wgettext_fmt!(
"%ls: New limit cannot be an empty string\n",
cmd
));
builtin_print_error_trailer(parser, streams.err, cmd);
return STATUS_INVALID_ARGS;
} else if wcscasecmp(w.argv[w.woptind], L!("unlimited")) == Ordering::Equal {
RLIM_INFINITY
} else if wcscasecmp(w.argv[w.woptind], L!("hard")) == Ordering::Equal {
match get(what, true) {
Some(limit) => limit,
None => return STATUS_CMD_ERROR,
}
} else if wcscasecmp(w.argv[w.woptind], L!("soft")) == Ordering::Equal {
match get(what, soft) {
Some(limit) => limit,
None => return STATUS_CMD_ERROR,
}
} else if let Ok(limit) = fish_wcstol(w.argv[w.woptind]) {
limit as rlim_t * get_multiplier(what)
} else {
streams.err.append(wgettext_fmt!(
"%ls: Invalid limit '%ls'\n",
cmd,
w.argv[w.woptind]
));
builtin_print_error_trailer(parser, streams.err, cmd);
return STATUS_INVALID_ARGS;
};
set_limit(what, hard, soft, new_limit, streams)
} }
/// Struct describing a resource limit. /// Struct describing a resource limit.
struct Resource { struct Resource {
resource: c_int, // resource ID resource: c_uint, // resource ID
desc: &'static wstr, // description of resource desc: &'static wstr, // description of resource
switch_char: char, // switch used on commandline to specify resource switch_char: char, // switch used on commandline to specify resource
multiplier: c_int, // the implicit multiplier used when setting getting values multiplier: c_uint, // the implicit multiplier used when setting getting values
}
impl Resource {
fn new(
resource: c_uint,
desc: &'static wstr,
switch_char: char,
multiplier: c_uint,
) -> Resource {
Resource {
resource,
desc,
switch_char,
multiplier,
}
}
} }
/// Array of resource_t structs, describing all known resource types. /// Array of resource_t structs, describing all known resource types.
const resource_arr: &[Resource] = &[]; static RESOURCE_ARR: Lazy<Box<[Resource]>> = Lazy::new(|| {
let resources_info = [
(
RLIMIT_SBSIZE(),
L!("Maximum size of socket buffers"),
'b',
1024,
),
(
RLIMIT_CORE(),
L!("Maximum size of core files created"),
'c',
1024,
),
(
RLIMIT_DATA(),
L!("Maximum size of a processs data segment"),
'd',
1024,
),
(
RLIMIT_NICE(),
L!("Control of maximum nice priority"),
'e',
1,
),
(
RLIMIT_FSIZE(),
L!("Maximum size of files created by the shell"),
'f',
1024,
),
(
RLIMIT_SIGPENDING(),
L!("Maximum number of pending signals"),
'i',
1,
),
(
RLIMIT_MEMLOCK(),
L!("Maximum size that may be locked into memory"),
'l',
1024,
),
(RLIMIT_RSS(), L!("Maximum resident set size"), 'm', 1024),
(
RLIMIT_NOFILE(),
L!("Maximum number of open file descriptors"),
'n',
1,
),
(
RLIMIT_MSGQUEUE(),
L!("Maximum bytes in POSIX message queues"),
'q',
1024,
),
(
RLIMIT_RTPRIO(),
L!("Maximum realtime scheduling priority"),
'r',
1,
),
(RLIMIT_STACK(), L!("Maximum stack size"), 's', 1024),
(
RLIMIT_CPU(),
L!("Maximum amount of CPU time in seconds"),
't',
1,
),
(
RLIMIT_NPROC(),
L!("Maximum number of processes available to current user"),
'u',
1,
),
(
RLIMIT_AS(),
L!("Maximum amount of virtual memory available to each process"),
'v',
1024,
),
(RLIMIT_SWAP(), L!("Maximum swap space"), 'w', 1024),
(
RLIMIT_RTTIME(),
L!("Maximum contiguous realtime CPU time"),
'y',
1,
),
(RLIMIT_KQUEUES(), L!("Maximum number of kqueues"), 'K', 1),
(
RLIMIT_NPTS(),
L!("Maximum number of pseudo-terminals"),
'P',
1,
),
(
RLIMIT_NTHR(),
L!("Maximum number of simultaneous threads"),
'T',
1,
),
];
let unknown = -1;
let mut resources = Vec::new();
for resource in resources_info {
let (resource, desc, switch_char, multiplier) = resource;
if resource != unknown {
resources.push(Resource::new(
resource as c_uint,
desc,
switch_char,
multiplier,
));
}
}
resources.into_boxed_slice()
});

View file

@ -5,6 +5,7 @@
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/resource.h>
#include <term.h> #include <term.h>
#include <unistd.h> #include <unistd.h>
@ -79,3 +80,122 @@ static const bool uvar_file_set_mtime_hack =
#endif #endif
#undef UVAR_FILE_SET_MTIME_HACK #undef UVAR_FILE_SET_MTIME_HACK
bool UVAR_FILE_SET_MTIME_HACK() { return uvar_file_set_mtime_hack; } bool UVAR_FILE_SET_MTIME_HACK() { return uvar_file_set_mtime_hack; }
int C_RLIMIT_CORE() { return RLIMIT_CORE; }
int C_RLIMIT_DATA() { return RLIMIT_DATA; }
int C_RLIMIT_FSIZE() { return RLIMIT_FSIZE; }
int C_RLIMIT_NOFILE() { return RLIMIT_NOFILE; }
int C_RLIMIT_STACK() { return RLIMIT_STACK; }
int C_RLIMIT_CPU() { return RLIMIT_CPU; }
int C_RLIMIT_SBSIZE() {
#ifdef RLIMIT_SBSIZE
return RLIMIT_SBSIZE;
#else
return -1;
#endif
}
int C_RLIMIT_NICE() {
#ifdef RLIMIT_NICE
return RLIMIT_NICE;
#else
return -1;
#endif
}
int C_RLIMIT_SIGPENDING() {
#ifdef RLIMIT_SIGPENDING
return RLIMIT_SIGPENDING;
#else
return -1;
#endif
}
int C_RLIMIT_MEMLOCK() {
#ifdef RLIMIT_MEMLOCK
return RLIMIT_MEMLOCK;
#else
return -1;
#endif
}
int C_RLIMIT_RSS() {
#ifdef RLIMIT_RSS
return RLIMIT_RSS;
#else
return -1;
#endif
}
int C_RLIMIT_MSGQUEUE() {
#ifdef RLIMIT_MSGQUEUE
return RLIMIT_MSGQUEUE;
#else
return -1;
#endif
}
int C_RLIMIT_RTPRIO() {
#ifdef RLIMIT_RTPRIO
return RLIMIT_RTPRIO;
#else
return -1;
#endif
}
int C_RLIMIT_NPROC() {
#ifdef RLIMIT_NPROC
return RLIMIT_NPROC;
#else
return -1;
#endif
}
int C_RLIMIT_AS() {
#ifdef RLIMIT_AS
return RLIMIT_AS;
#else
return -1;
#endif
}
int C_RLIMIT_SWAP() {
#ifdef RLIMIT_SWAP
return RLIMIT_SWAP;
#else
return -1;
#endif
}
int C_RLIMIT_RTTIME() {
#ifdef RLIMIT_RTTIME
return RLIMIT_RTTIME;
#else
return -1;
#endif
}
int C_RLIMIT_KQUEUES() {
#ifdef RLIMIT_KQUEUES
return RLIMIT_KQUEUES;
#else
return -1;
#endif
}
int C_RLIMIT_NPTS() {
#ifdef RLIMIT_NPTS
return RLIMIT_NPTS;
#else
return -1;
#endif
}
int C_RLIMIT_NTHR() {
#ifdef RLIMIT_NTHR
return RLIMIT_NTHR;
#else
return -1;
#endif
}

View file

@ -51,3 +51,55 @@ extern "C" {
pub fn stdout_stream() -> *mut libc::FILE; pub fn stdout_stream() -> *mut libc::FILE;
pub fn UVAR_FILE_SET_MTIME_HACK() -> bool; pub fn UVAR_FILE_SET_MTIME_HACK() -> bool;
} }
macro_rules! CVAR {
($cfn:ident, $cvar:ident, $type:ident) => {
pub fn $cvar() -> $type {
unsafe { $cfn() }
}
};
}
CVAR!(C_RLIMIT_SBSIZE, RLIMIT_SBSIZE, i32);
CVAR!(C_RLIMIT_CORE, RLIMIT_CORE, i32);
CVAR!(C_RLIMIT_DATA, RLIMIT_DATA, i32);
CVAR!(C_RLIMIT_NICE, RLIMIT_NICE, i32);
CVAR!(C_RLIMIT_FSIZE, RLIMIT_FSIZE, i32);
CVAR!(C_RLIMIT_SIGPENDING, RLIMIT_SIGPENDING, i32);
CVAR!(C_RLIMIT_MEMLOCK, RLIMIT_MEMLOCK, i32);
CVAR!(C_RLIMIT_RSS, RLIMIT_RSS, i32);
CVAR!(C_RLIMIT_NOFILE, RLIMIT_NOFILE, i32);
CVAR!(C_RLIMIT_MSGQUEUE, RLIMIT_MSGQUEUE, i32);
CVAR!(C_RLIMIT_RTPRIO, RLIMIT_RTPRIO, i32);
CVAR!(C_RLIMIT_STACK, RLIMIT_STACK, i32);
CVAR!(C_RLIMIT_CPU, RLIMIT_CPU, i32);
CVAR!(C_RLIMIT_NPROC, RLIMIT_NPROC, i32);
CVAR!(C_RLIMIT_AS, RLIMIT_AS, i32);
CVAR!(C_RLIMIT_SWAP, RLIMIT_SWAP, i32);
CVAR!(C_RLIMIT_RTTIME, RLIMIT_RTTIME, i32);
CVAR!(C_RLIMIT_KQUEUES, RLIMIT_KQUEUES, i32);
CVAR!(C_RLIMIT_NPTS, RLIMIT_NPTS, i32);
CVAR!(C_RLIMIT_NTHR, RLIMIT_NTHR, i32);
extern "C" {
fn C_RLIMIT_SBSIZE() -> i32; // ifndef: -1
fn C_RLIMIT_CORE() -> i32;
fn C_RLIMIT_DATA() -> i32;
fn C_RLIMIT_NICE() -> i32; // ifndef: -1
fn C_RLIMIT_FSIZE() -> i32;
fn C_RLIMIT_SIGPENDING() -> i32; // ifndef: -1
fn C_RLIMIT_MEMLOCK() -> i32; // ifndef: -1
fn C_RLIMIT_RSS() -> i32; // ifndef: -1
fn C_RLIMIT_NOFILE() -> i32;
fn C_RLIMIT_MSGQUEUE() -> i32; // ifndef: -1
fn C_RLIMIT_RTPRIO() -> i32; // ifndef: -1
fn C_RLIMIT_STACK() -> i32;
fn C_RLIMIT_CPU() -> i32;
fn C_RLIMIT_NPROC() -> i32; // ifndef: -1
fn C_RLIMIT_AS() -> i32; // ifndef: -1
fn C_RLIMIT_SWAP() -> i32; // ifndef: -1
fn C_RLIMIT_RTTIME() -> i32; // ifndef: -1
fn C_RLIMIT_KQUEUES() -> i32; // ifndef: -1
fn C_RLIMIT_NPTS() -> i32; // ifndef: -1
fn C_RLIMIT_NTHR() -> i32; // ifndef: -1
}

View file

@ -45,7 +45,6 @@ include_cpp! {
#include "builtins/bind.h" #include "builtins/bind.h"
#include "builtins/commandline.h" #include "builtins/commandline.h"
#include "builtins/ulimit.h"
safety!(unsafe_ffi) safety!(unsafe_ffi)
@ -72,7 +71,6 @@ include_cpp! {
generate!("builtin_bind") generate!("builtin_bind")
generate!("builtin_commandline") generate!("builtin_commandline")
generate!("builtin_ulimit")
generate!("init_input") generate!("init_input")

View file

@ -1,451 +0,0 @@
// Functions used for implementing the ulimit builtin.
#include "config.h" // IWYU pragma: keep
#include "ulimit.h"
#include <sys/resource.h>
#include <algorithm>
#include <cerrno>
#include <cwchar>
#include "../builtin.h"
#include "../common.h"
#include "../fallback.h" // IWYU pragma: keep
#include "../io.h"
#include "../maybe.h"
#include "../wgetopt.h"
#include "../wutil.h" // IWYU pragma: keep
#include "builtins/shared.rs.h"
#include "builtins/ulimit.h"
/// Struct describing a resource limit.
struct resource_t {
int resource; // resource ID
const wchar_t *desc; // description of resource
wchar_t switch_char; // switch used on commandline to specify resource
int multiplier; // the implicit multiplier used when setting getting values
};
/// Array of resource_t structs, describing all known resource types.
static const struct resource_t resource_arr[] = {
#ifdef RLIMIT_SBSIZE
{RLIMIT_SBSIZE, L"Maximum size of socket buffers", L'b', 1024},
#endif
{RLIMIT_CORE, L"Maximum size of core files created", L'c', 1024},
{RLIMIT_DATA, L"Maximum size of a processs data segment", L'd', 1024},
#ifdef RLIMIT_NICE
{RLIMIT_NICE, L"Control of maximum nice priority", L'e', 1},
#endif
{RLIMIT_FSIZE, L"Maximum size of files created by the shell", L'f', 1024},
#ifdef RLIMIT_SIGPENDING
{RLIMIT_SIGPENDING, L"Maximum number of pending signals", L'i', 1},
#endif
#ifdef RLIMIT_MEMLOCK
{RLIMIT_MEMLOCK, L"Maximum size that may be locked into memory", L'l', 1024},
#endif
#ifdef RLIMIT_RSS
{RLIMIT_RSS, L"Maximum resident set size", L'm', 1024},
#endif
{RLIMIT_NOFILE, L"Maximum number of open file descriptors", L'n', 1},
#ifdef RLIMIT_MSGQUEUE
{RLIMIT_MSGQUEUE, L"Maximum bytes in POSIX message queues", L'q', 1024},
#endif
#ifdef RLIMIT_RTPRIO
{RLIMIT_RTPRIO, L"Maximum realtime scheduling priority", L'r', 1},
#endif
{RLIMIT_STACK, L"Maximum stack size", L's', 1024},
{RLIMIT_CPU, L"Maximum amount of CPU time in seconds", L't', 1},
#ifdef RLIMIT_NPROC
{RLIMIT_NPROC, L"Maximum number of processes available to current user", L'u', 1},
#endif
#ifdef RLIMIT_AS
{RLIMIT_AS, L"Maximum amount of virtual memory available to each process", L'v', 1024},
#endif
#ifdef RLIMIT_SWAP
{RLIMIT_SWAP, L"Maximum swap space", L'w', 1024},
#endif
#ifdef RLIMIT_RTTIME
{RLIMIT_RTTIME, L"Maximum contiguous realtime CPU time", L'y', 1},
#endif
#ifdef RLIMIT_KQUEUES
{RLIMIT_KQUEUES, L"Maximum number of kqueues", L'K', 1},
#endif
#ifdef RLIMIT_NPTS
{RLIMIT_NPTS, L"Maximum number of pseudo-terminals", L'P', 1},
#endif
#ifdef RLIMIT_NTHR
{RLIMIT_NTHR, L"Maximum number of simultaneous threads", L'T', 1},
#endif
{0, nullptr, 0, 0}};
/// This is likely to be the same as RLIMIT_INFINITY, but it shouldn't get used
/// in the same context (that is, compared to the result of a getrlimit call).
#define RLIMIT_UNKNOWN -1
/// Get the implicit multiplication factor for the specified resource limit.
static int get_multiplier(int what) {
for (int i = 0; resource_arr[i].desc; i++) {
if (resource_arr[i].resource == what) {
return resource_arr[i].multiplier;
}
}
return -1;
}
/// Return the value for the specified resource limit. This function does _not_ multiply the limit
/// value by the multiplier constant used by the commandline ulimit.
static rlim_t get(int resource, int hard) {
struct rlimit ls;
getrlimit(resource, &ls);
return hard ? ls.rlim_max : ls.rlim_cur;
}
/// Print the value of the specified resource limit.
static void print(int resource, int hard, io_streams_t &streams) {
rlim_t l = get(resource, hard);
if (l == RLIM_INFINITY)
streams.out()->append(format_string(L"unlimited\n"));
else
streams.out()->append(format_string(L"%lu\n", l / get_multiplier(resource)));
}
/// Print values of all resource limits.
static void print_all(int hard, io_streams_t &streams) {
int i;
int w = 0;
for (i = 0; resource_arr[i].desc; i++) {
w = std::max(w, fish_wcswidth(resource_arr[i].desc));
}
for (i = 0; resource_arr[i].desc; i++) {
struct rlimit ls;
rlim_t l;
getrlimit(resource_arr[i].resource, &ls);
l = hard ? ls.rlim_max : ls.rlim_cur;
const wchar_t *unit =
((resource_arr[i].resource == RLIMIT_CPU)
? L"(seconds, "
: (get_multiplier(resource_arr[i].resource) == 1 ? L"(" : L"(kB, "));
streams.out()->append(format_string(L"%-*ls %10ls-%lc) ", w, resource_arr[i].desc, unit,
resource_arr[i].switch_char));
if (l == RLIM_INFINITY) {
streams.out()->append(format_string(L"unlimited\n"));
} else {
streams.out()->append(
format_string(L"%lu\n", l / get_multiplier(resource_arr[i].resource)));
}
}
}
/// Returns the description for the specified resource limit.
static const wchar_t *get_desc(int what) {
int i;
for (i = 0; resource_arr[i].desc; i++) {
if (resource_arr[i].resource == what) {
return resource_arr[i].desc;
}
}
return L"Not a resource";
}
/// Set the new value of the specified resource limit. This function does _not_ multiply the limit
// value by the multiplier constant used by the commandline ulimit.
static int set_limit(int resource, int hard, int soft, rlim_t value, io_streams_t &streams) {
struct rlimit ls;
getrlimit(resource, &ls);
if (hard) ls.rlim_max = value;
if (soft) {
ls.rlim_cur = value;
// Do not attempt to set the soft limit higher than the hard limit.
if ((value == RLIM_INFINITY && ls.rlim_max != RLIM_INFINITY) ||
(value != RLIM_INFINITY && ls.rlim_max != RLIM_INFINITY && value > ls.rlim_max)) {
ls.rlim_cur = ls.rlim_max;
}
}
if (setrlimit(resource, &ls)) {
if (errno == EPERM) {
streams.err()->append(
format_string(L"ulimit: Permission denied when changing resource of type '%ls'\n",
get_desc(resource)));
} else {
builtin_wperror(L"ulimit", streams);
}
return STATUS_CMD_ERROR;
}
return STATUS_CMD_OK;
}
/// The ulimit builtin, used for setting resource limits.
int builtin_ulimit(const void *_parser, void *_streams, void *_argv) {
const auto &parser = *static_cast<const parser_t *>(_parser);
auto &streams = *static_cast<io_streams_t *>(_streams);
auto argv = static_cast<const wchar_t **>(_argv);
const wchar_t *cmd = argv[0];
int argc = builtin_count_args(argv);
bool report_all = false;
bool hard = false;
bool soft = false;
int what = RLIMIT_FSIZE;
static const wchar_t *const short_options = L":HSabcdefilmnqrstuvwyKPTh";
static const struct woption long_options[] = {{L"all", no_argument, 'a'},
{L"hard", no_argument, 'H'},
{L"soft", no_argument, 'S'},
{L"socket-buffers", no_argument, 'b'},
{L"core-size", no_argument, 'c'},
{L"data-size", no_argument, 'd'},
{L"nice", no_argument, 'e'},
{L"file-size", no_argument, 'f'},
{L"pending-signals", no_argument, 'i'},
{L"lock-size", no_argument, 'l'},
{L"resident-set-size", no_argument, 'm'},
{L"file-descriptor-count", no_argument, 'n'},
{L"queue-size", no_argument, 'q'},
{L"realtime-priority", no_argument, 'r'},
{L"stack-size", no_argument, 's'},
{L"cpu-time", no_argument, 't'},
{L"process-count", no_argument, 'u'},
{L"virtual-memory-size", no_argument, 'v'},
{L"swap-size", no_argument, 'w'},
{L"realtime-maxtime", no_argument, 'y'},
{L"kernel-queues", no_argument, 'K'},
{L"ptys", no_argument, 'P'},
{L"threads", no_argument, 'T'},
{L"help", no_argument, 'h'},
{}};
int opt;
wgetopter_t w;
while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, nullptr)) != -1) {
switch (opt) {
case 'a': {
report_all = true;
break;
}
case 'H': {
hard = true;
break;
}
case 'S': {
soft = true;
break;
}
case 'b': {
#ifdef RLIMIT_SBSIZE
what = RLIMIT_SBSIZE;
#else
what = RLIMIT_UNKNOWN;
#endif
break;
}
case 'c': {
what = RLIMIT_CORE;
break;
}
case 'd': {
what = RLIMIT_DATA;
break;
}
case 'e': {
#ifdef RLIMIT_NICE
what = RLIMIT_NICE;
#else
what = RLIMIT_UNKNOWN;
#endif
break;
}
case 'f': {
what = RLIMIT_FSIZE;
break;
}
case 'i': {
#ifdef RLIMIT_SIGPENDING
what = RLIMIT_SIGPENDING;
#else
what = RLIMIT_UNKNOWN;
#endif
break;
}
case 'l': {
#ifdef RLIMIT_MEMLOCK
what = RLIMIT_MEMLOCK;
#else
what = RLIMIT_UNKNOWN;
#endif
break;
}
case 'm': {
#ifdef RLIMIT_RSS
what = RLIMIT_RSS;
#else
what = RLIMIT_UNKNOWN;
#endif
break;
}
case 'n': {
what = RLIMIT_NOFILE;
break;
}
case 'q': {
#ifdef RLIMIT_MSGQUEUE
what = RLIMIT_MSGQUEUE;
#else
what = RLIMIT_UNKNOWN;
#endif
break;
}
case 'r': {
#ifdef RLIMIT_RTPRIO
what = RLIMIT_RTPRIO;
#else
what = RLIMIT_UNKNOWN;
#endif
break;
}
case 's': {
what = RLIMIT_STACK;
break;
}
case 't': {
what = RLIMIT_CPU;
break;
}
case 'u': {
#ifdef RLIMIT_NPROC
what = RLIMIT_NPROC;
#else
what = RLIMIT_UNKNOWN;
#endif
break;
}
case 'v': {
#ifdef RLIMIT_AS
what = RLIMIT_AS;
#else
what = RLIMIT_UNKNOWN;
#endif
break;
}
case 'w': {
#ifdef RLIMIT_SWAP
what = RLIMIT_SWAP;
#else
what = RLIMIT_UNKNOWN;
#endif
break;
}
case 'y': {
#ifdef RLIMIT_RTTIME
what = RLIMIT_RTTIME;
#else
what = RLIMIT_UNKNOWN;
#endif
break;
}
case 'K': {
#ifdef RLIMIT_KQUEUES
what = RLIMIT_KQUEUES;
#else
what = RLIMIT_UNKNOWN;
#endif
break;
}
case 'P': {
#ifdef RLIMIT_NPTS
what = RLIMIT_NPTS;
#else
what = RLIMIT_UNKNOWN;
#endif
break;
}
case 'T': {
#ifdef RLIMIT_NTHR
what = RLIMIT_NTHR;
#else
what = RLIMIT_UNKNOWN;
#endif
break;
}
case 'h': {
builtin_print_help(parser, streams, cmd);
return STATUS_CMD_OK;
}
case ':': {
builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1], true);
return STATUS_INVALID_ARGS;
}
case '?': {
builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1], true);
return STATUS_INVALID_ARGS;
}
default: {
DIE("unexpected retval from wgetopt_long");
}
}
}
if (report_all) {
print_all(hard, streams);
return STATUS_CMD_OK;
}
if (what == RLIMIT_UNKNOWN) {
streams.err()->append(
format_string(_(L"%ls: Resource limit not available on this operating system\n"), cmd));
builtin_print_error_trailer(parser, *streams.err(), cmd);
return STATUS_INVALID_ARGS;
}
int arg_count = argc - w.woptind;
if (arg_count == 0) {
// Show current limit value.
print(what, hard, streams);
return STATUS_CMD_OK;
} else if (arg_count != 1) {
streams.err()->append(format_string(BUILTIN_ERR_TOO_MANY_ARGUMENTS, cmd));
builtin_print_error_trailer(parser, *streams.err(), cmd);
return STATUS_INVALID_ARGS;
}
// Change current limit value.
if (!hard && !soft) {
// Set both hard and soft limits if neither was specified.
hard = soft = true;
}
rlim_t new_limit;
if (*argv[w.woptind] == L'\0') {
streams.err()->append(format_string(_(L"%ls: New limit cannot be an empty string\n"), cmd));
builtin_print_error_trailer(parser, *streams.err(), cmd);
return STATUS_INVALID_ARGS;
} else if (wcscasecmp(argv[w.woptind], L"unlimited") == 0) {
new_limit = RLIM_INFINITY;
} else if (wcscasecmp(argv[w.woptind], L"hard") == 0) {
new_limit = get(what, 1);
} else if (wcscasecmp(argv[w.woptind], L"soft") == 0) {
new_limit = get(what, soft);
} else {
new_limit = fish_wcstol(argv[w.woptind]);
if (errno) {
streams.err()->append(
format_string(_(L"%ls: Invalid limit '%ls'\n"), cmd, argv[w.woptind]));
builtin_print_error_trailer(parser, *streams.err(), cmd);
return STATUS_INVALID_ARGS;
}
new_limit *= get_multiplier(what);
}
return set_limit(what, hard, soft, new_limit, streams);
}

View file

@ -1,13 +0,0 @@
// Prototypes for functions for executing builtin_ulimit functions.
#ifndef FISH_BUILTIN_ULIMIT_H
#define FISH_BUILTIN_ULIMIT_H
#include "../maybe.h"
struct Parser;
struct IoStreams;
using parser_t = Parser;
using io_streams_t = IoStreams;
int builtin_ulimit(const void *parser, void *streams, void *argv);
#endif

View file

@ -1,7 +1,6 @@
#include "builtin.h" #include "builtin.h"
#include "builtins/bind.h" #include "builtins/bind.h"
#include "builtins/commandline.h" #include "builtins/commandline.h"
#include "builtins/ulimit.h"
#include "event.h" #include "event.h"
#include "fds.h" #include "fds.h"
#include "fish_indent_common.h" #include "fish_indent_common.h"
@ -40,5 +39,4 @@ void mark_as_used(const parser_t& parser, env_stack_t& env_stack) {
builtin_bind({}, {}, {}); builtin_bind({}, {}, {});
builtin_commandline({}, {}, {}); builtin_commandline({}, {}, {});
builtin_ulimit({}, {}, {});
} }