#!/bin/bash

## Copyright (C) 2020 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

## If you modify limit-low, also consider similar modification to other
## 'limit-*' wrapper scripts such as limit-medium and limit-high.

## https://forums.whonix.org/t/constrained-system-resources-program-starter-wrapper/10914

## limit-low design goals:
## - Do one thing and do it well.
## - The one thing is to be a wrapper that limits system resources for wrapped
##   applications.
## - Support being run as:
##   - user
##   - root
##   - in shell or script in pipes
##   - stdout, stderr, stdin interactive shell input/output
##   - graphical (GUI) applications
## - Being as non-intrusive as possible. For example, not adding extraneous
##   output to stdout such as
##   "Running scope as unit: run-r0d607a8f35dc4dea909b830f9d922b99.scope".

## usage examples:
##
## run system resources limited as user:
## limit-low stress --cpu 8 --io 4 --vm 2 --vm-bytes 128M --timeout 10s
##
## run system resources limited application as root:
## sudo limit-low stress --cpu 8 --io 4 --vm 2 --vm-bytes 128M --timeout 10s
##
## use inside pipes:
## echo a | limit-low grep a
##
## interactive use:
## limit-low nano file-name

## constrained system resources program starter wrapper
## https://github.com/systemd/systemd/issues/16180

set -e

source "${HELPER_SCRIPTS_PATH:-}"/usr/libexec/helper-scripts/strings.bsh

ionice_class=3
ionice_classdata=""
niceness=19
percent_memory=20

if [[ "$@" = "" ]]; then
   base_name="${0##*/}"
   echo "$base_name: ERROR: syntax:" >&2
   echo "$base_name application [parameters]" >&2
   exit 1
fi

if ! is_whole_number "$maxmem"; then
   ## Meaningless in Qubes since Qubes dynamically assigns RAM as needed.
   mem_available="$(cat /proc/meminfo | grep MemAvailable | cut -d ":" -f 2)"
   mem_available="$(echo "$mem_available" | str_replace "kB" "")"
   ## mem_available is in KB already.
   fraction="$(( $mem_available * $percent_memory / 100 ))"
else
   ## maxmem is MB
   ## convert to kB
   fraction="$(( $maxmem * 1000 ))"
fi

## -m   The maximum resident set size.
ulimit -m "$fraction"

## -v   The maximum amount of virtual memory available to the process.
## Crash torbrowser, possibly other applications.
## https://superuser.com/questions/472587/why-does-limiting-my-virtual-memory-to-512mb-with-ulimit-v-crash-the-jvm
#ulimit -v "$fraction"

## -e   The maximum scheduling priority ("nice")
## Already set to 0 on Debian by default.
## Hence, cannot be further lowered per application.
## https://superuser.com/questions/934113/real-time-option-in-ulimit
## https://unix.stackexchange.com/questions/8983/set-default-nice-value-for-a-given-user-limits-conf
#ulimit -e 10

## -u   The maximum number of processes available to a single user.
## The maximum number of processes available
ulimit -u 300

## -a   All current limits are reported.
#ulimit -a

if [ "$ionice_classdata" = "" ]; then
   ## ionice: ignoring given class data for idle class
   ionice --class "$ionice_class" \
   nice -n "$niceness" \
   "$@"
else
   ionice --class "$ionice_class" --classdata "$ionice_classdata" \
   nice -n "$niceness" \
   "$@"
fi
