aboutsummaryrefslogblamecommitdiff
path: root/doomsh
blob: 4133ff720cf6b6243389eae76d291dc3ebb8b548 (plain) (tree)








































































































































                                                                               
#!/usr/bin/env bash

#
# doomsh: Play with low ulimits in Bash
#
# Author: Tom Ryder
# Copyright: 2016
# License: MIT (see LICENSE.markdown)
#
self=doomsh

# Don't try to play doomsh if already playing doomsh
if [[ $DOOMSH ]] ; then
    printf '%s: Already playing doomsh!\n' \
        "$self" >&2
    exit 2
fi

# Show the coloured logo; thanks to Frans P. de Vries for the classic ASCII art
# <http://www.gamers.org/~fpv/doombann.html>
printf >&2 %s "$( {
    tput AF 3 || tput setaf 3
    tput md || tput bold
} 2>/dev/null )"
while IFS= read -r line ; do
    printf >&2 '%s\n' "$line"
done <<EOF
______ _____ _____ __   ___
| __  \  _  |  _  |  \ /  |
| | \ | | | | | | | . V . |
| | / | \_/ | \_/ | |\ /| |
| |/ / \   / \   /| | V | |
| ' /   \_/   \_/ \_|   | |
|__/                    \_|
EOF
printf >&2 %s "$( {
    tput me || tput sgr0
} 2>/dev/null )"

# Need to figure out a skill level
declare -i skill

# If we have an argument, that must be the skill level
if (($#)) ; then
    skill=$1
    shift

# Otherwise, we'll give the user a menu to choose from
else
    while IFS= read -r line ; do
        printf >&2 '%s\n' "$line"
    done <<EOF

1. I'm too young to die
2. Hey, not too rough
3. Hurt me plenty
4. Ultra-Violence
5. Nightmare!

EOF
    read -er -p '[1-5]: ' skill

    # If the user chose "Nightmare!" mode, get them to confirm, in the true
    # spirit of things; otherwise, quit
    if ((skill == 5)) ; then
        while IFS= read -r line ; do
            printf '%s\n' "$line" >&2
        done <<EOF

Are you sure? This skill level isn't even remotely fair.
EOF
        read -er -p '[y/N]: ' confirm
        [[ $confirm == [Yy]* ]] || exit 1
    fi
fi

# Don't accept any other skill level settings
case $skill in
    1|2|3|4|5)
        ;;
    *)
        printf '%s: Unknown skill level\n' \
            "$self" >&2
        exit 2
esac

# Form a red prompt (hopefully) using terminal escapes
ps1=$(printf '%sdoomsh[%u]\$%s ' \
    "$( {
        tput AF 1 || tput setaf 1
    } 2>/dev/null )" \
    "$skill" \
    "$( {
        tput me || tput sgr0
    } 2>/dev/null )"
)

# Set the limits based on the skill level
declare -i filesize files stack memory
filesize=$((1 << (8 - skill)))
files=$((11 - skill))
stack=$((1 << (11 - skill)))
memory=$((1 << (19 - skill)))

# Display the limits
printf >&2 '\n'
printf >&2 'File size writing limit: %u blocks\n' \
    "$filesize"
printf >&2 'Open files limit: %u\n' \
    "$files"
printf >&2 'Stack size: %u kbytes\n' \
    "$stack"
printf >&2 'Memory size: %u kbytes\n' \
    "$memory"
printf >&2 '\n'

# Record the current seconds since shell start, so we can tell how long the
# user lasts before giving up or segfaulting
declare -i start
start=$SECONDS

# Open a subshell so that the ulimit settings don't persist after the fork
(
    # We only proceed if the ulimit call succeeds
    ulimit \
        -f "$filesize" \
        -n "$files" \
        -s "$((1 << (10 - skill)))" \
        -v "$((1 << (19 - skill)))" \
        || exit 2
    DOOMSH=$skill PS1=$ps1 bash --norc
)

# Give the user some statistics about their performance
printf >&2 '%s: You lasted %u seconds at skill level %u!\n' \
    "$self" "$((SECONDS - start))" "$skill"