diff --git a/CMakeLists.txt b/CMakeLists.txt index 5619070c7..305166c81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,7 +101,6 @@ endif() set(FISH_BUILTIN_SRCS src/builtins/bind.cpp src/builtins/commandline.cpp - src/builtins/ulimit.cpp ) # List of other sources. set(FISH_SRCS diff --git a/Cargo.toml b/Cargo.toml index e9bb87b76..2761ae335 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,7 @@ lazy_static = "1.4.0" libc = "0.2.137" lru = "0.10.0" 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" # to make integer->enum conversion easier num-derive = "0.3.3" diff --git a/fish-rust/src/builtins/ulimit.rs b/fish-rust/src/builtins/ulimit.rs index aa2b0621a..826b6a22e 100644 --- a/fish-rust/src/builtins/ulimit.rs +++ b/fish-rust/src/builtins/ulimit.rs @@ -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::*; +/// 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 { + 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 { + 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 { - 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 Resource { - resource: c_int, // resource ID + resource: c_uint, // resource ID desc: &'static wstr, // description of 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. -const resource_arr: &[Resource] = &[]; +static RESOURCE_ARR: Lazy> = 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 process’s 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() +}); diff --git a/fish-rust/src/compat.c b/fish-rust/src/compat.c index 581c25c1a..cd4c78076 100644 --- a/fish-rust/src/compat.c +++ b/fish-rust/src/compat.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -79,3 +80,122 @@ static const bool uvar_file_set_mtime_hack = #endif #undef 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 +} diff --git a/fish-rust/src/compat.rs b/fish-rust/src/compat.rs index 07e2a4ffd..be529e65a 100644 --- a/fish-rust/src/compat.rs +++ b/fish-rust/src/compat.rs @@ -51,3 +51,55 @@ extern "C" { pub fn stdout_stream() -> *mut libc::FILE; 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 +} diff --git a/fish-rust/src/ffi.rs b/fish-rust/src/ffi.rs index f47e32ab2..872d522c5 100644 --- a/fish-rust/src/ffi.rs +++ b/fish-rust/src/ffi.rs @@ -45,7 +45,6 @@ include_cpp! { #include "builtins/bind.h" #include "builtins/commandline.h" - #include "builtins/ulimit.h" safety!(unsafe_ffi) @@ -72,7 +71,6 @@ include_cpp! { generate!("builtin_bind") generate!("builtin_commandline") - generate!("builtin_ulimit") generate!("init_input") diff --git a/src/builtins/ulimit.cpp b/src/builtins/ulimit.cpp deleted file mode 100644 index c6ce01076..000000000 --- a/src/builtins/ulimit.cpp +++ /dev/null @@ -1,451 +0,0 @@ -// Functions used for implementing the ulimit builtin. -#include "config.h" // IWYU pragma: keep - -#include "ulimit.h" - -#include - -#include -#include -#include - -#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 process’s 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(_parser); - auto &streams = *static_cast(_streams); - auto argv = static_cast(_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); -} diff --git a/src/builtins/ulimit.h b/src/builtins/ulimit.h deleted file mode 100644 index 1707542f8..000000000 --- a/src/builtins/ulimit.h +++ /dev/null @@ -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 diff --git a/src/ffi_baggage.h b/src/ffi_baggage.h index 1787fea18..2869e68af 100644 --- a/src/ffi_baggage.h +++ b/src/ffi_baggage.h @@ -1,7 +1,6 @@ #include "builtin.h" #include "builtins/bind.h" #include "builtins/commandline.h" -#include "builtins/ulimit.h" #include "event.h" #include "fds.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_commandline({}, {}, {}); - builtin_ulimit({}, {}, {}); }