diff options
Diffstat (limited to 'includes/limit.sh')
-rw-r--r-- | includes/limit.sh | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/includes/limit.sh b/includes/limit.sh new file mode 100644 index 00000000..2a1545b6 --- /dev/null +++ b/includes/limit.sh @@ -0,0 +1,107 @@ +#!/bin/bash +# +# Resource limiting wrapper for command execution +# +# Why is this in shell script? Because bash has a setrlimit() wrapper +# and is available on most Linux systems. If Perl was distributed with +# BSD::Resource included, we would happily use that instead, but it isn't. + +MW_INCLUDE_STDERR= +MW_CPU_LIMIT=0 +MW_CGROUP= +MW_MEM_LIMIT=0 +MW_FILE_SIZE_LIMIT=0 +MW_WALL_CLOCK_LIMIT=0 + +# Override settings +eval "$2" + +if [ -n "$MW_INCLUDE_STDERR" ]; then + exec 2>&1 +fi + +if [ "$MW_CPU_LIMIT" -gt 0 ]; then + ulimit -t "$MW_CPU_LIMIT" +fi +if [ "$MW_MEM_LIMIT" -gt 0 ]; then + if [ -n "$MW_CGROUP" ]; then + # Create cgroup + if ! mkdir -m 0700 "$MW_CGROUP"/$$; then + echo "limit.sh: failed to create the cgroup." 1>&2 + exit 1 + fi + echo $$ > "$MW_CGROUP"/$$/tasks + if [ -n "$MW_CGROUP_NOTIFY" ]; then + echo "1" > "$MW_CGROUP"/$$/notify_on_release + fi + # Memory + echo $(($MW_MEM_LIMIT*1024)) > "$MW_CGROUP"/$$/memory.limit_in_bytes + # Memory+swap + echo $(($MW_MEM_LIMIT*1024)) > "$MW_CGROUP"/$$/memory.memsw.limit_in_bytes + else + ulimit -v "$MW_MEM_LIMIT" + fi +else + MW_CGROUP="" +fi +if [ "$MW_FILE_SIZE_LIMIT" -gt 0 ]; then + ulimit -f "$MW_FILE_SIZE_LIMIT" +fi +if [ "$MW_WALL_CLOCK_LIMIT" -gt 0 -a -x "/usr/bin/timeout" ]; then + /usr/bin/timeout $MW_WALL_CLOCK_LIMIT /bin/bash -c "$1" + STATUS="$?" + if [ "$STATUS" == 124 ]; then + echo "limit.sh: timed out." 1>&2 + fi +else + eval "$1" + STATUS="$?" +fi + +# Clean up cgroup +cleanup() { + # First we have to move the current task into a "garbage" group, otherwise + # the cgroup will not be empty, and attempting to remove it will fail with + # "Device or resource busy" + if [ -w "$MW_CGROUP"/tasks ]; then + GARBAGE="$MW_CGROUP" + else + GARBAGE="$MW_CGROUP"/garbage-"$USER" + if [ ! -e "$GARBAGE" ]; then + mkdir -m 0700 "$GARBAGE" + fi + fi + echo $BASHPID > "$GARBAGE"/tasks + + # Suppress errors in case the cgroup has disappeared due to a release script + rmdir "$MW_CGROUP"/$$ 2>/dev/null +} + +updateTaskCount() { + # There are lots of ways to count lines in a file in shell script, but this + # is one of the few that doesn't create another process, which would + # increase the returned number of tasks. + readarray < "$MW_CGROUP"/$$/tasks + NUM_TASKS=${#MAPFILE[*]} +} + +if [ -n "$MW_CGROUP" ]; then + updateTaskCount + + if [ $NUM_TASKS -gt 1 ]; then + # Spawn a monitor process which will continue to poll for completion + # of all processes in the cgroup after termination of the parent shell + ( + while [ $NUM_TASKS -gt 1 ]; do + sleep 10 + updateTaskCount + done + cleanup + ) >&/dev/null < /dev/null & + disown -a + else + cleanup + fi +fi +exit "$STATUS" + |