summaryrefslogtreecommitdiff
path: root/dot/local/bin/sliceweasel
blob: 09c45bca895356a56865e45d4894c18736e8583f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#!/bin/bash
VERBOSE=y

command=firefox
memory_ratio=0.5
control_group=$command

as_root=sudo

total_memory() { free -b | (read line; read Mem total _; echo $total); }
out() { $as_root tee "$@" >/dev/null; }

die() { printf 'Error: %s\n' "$*" >&2; exit 1; }
verbose() { [ ! "$VERBOSE" ] || printf '%s\n' "$*" >&2; }

total_memory=$(total_memory) || die "could not ascertain total system memory; probable bug"
memory_limit_in_bytes=$(bc -q <<< "$total_memory * $memory_ratio / 1" ) || die "process 'bc' failed.  Is it installed?"

init_control_group()
{
    control_group_dir=/sys/fs/cgroup/"$1"/"$control_group"
    [ -d "$control_group_dir" ] && return
    $as_root mkdir "$control_group_dir" || die "mkdir failed"
    set_memory_limit $memory_limit_in_bytes || die "set_memory_limit failed"
}

get_cpu_limit()
{
    false
}

get_memory_limit()
{
    cat "$control_group_dir"/memory.limit_in_bytes
}

set_memory_limit()
{
    prev=$(get_memory_limit)
    if [ "$1" -ne "$prev" ]; then
        out "$control_group_dir"/memory.limit_in_bytes <<< $1
        verbose "changed memory limit ($prev -> $1)"
    else
        verbose "memory limit unchanged ($prev)"
    fi
}

join_control_group()
{
    out "$control_group_dir"/cgroup.procs <<< $$
}

get_cgroup()
{
    local key="$1" pid="${2:-$$}" line val
    while read line; do
        line=${line#*:}
        val=${line#$key:}
        [ "$val" != "$line" ] || continue
        echo "$val"
        return
    done < /proc/"$pid"/cgroup
    false
}

launch_command()
{
    init_control_group memory               || die "init_control_group failed"
    cg=$(get_cgroup memory)                 || die "failed to ascertain current control group"
    [ "$cg" = / ]                           || die "current control group should be '/', but is instead '$cg'"
    join_control_group                      || die "join_control_group failed"
    exec $command "$@"
}

pretty_show_memory_limit()
{
    local memory_limit="$(get_memory_limit)"
    printf "current memory limit: %d bytes (%d%%)\n" \
           "$memory_limit" \
           "$((memory_limit * 100 / total_memory))"
}

recpu_control_group()
{
    init_control_group cpu
    case "$1" in
        [0-9]*%) set_cpu_limit $((total_cpu * "${1%\%}" / 100)) ;;
        [0-9]*) set_cpu_limit "$1" ;;
        [-+*/][0-9]*) set_cpu_limit $(( $(get_cpu_limit) "$1" )) ;;
    esac
}

resize_control_group()
{
    init_control_group memory
    case "$1" in
        '') pretty_show_memory_limit >&2 ;;
        [0-9]*%) set_memory_limit $((total_memory * "${1%\%}" / 100)) ;;
        [0-9]*) set_memory_limit "$1" ;;
        [-+*/][0-9]*) set_memory_limit $(( $(get_memory_limit) "$1" )) ;;
    esac
}

usage()
{
    cat >&2 <<EOF
usage: ${0##*/} [--resize|--resize <bytes>|--resize=<bytes>] [--] [pass-through-options] [...]
       ${0##*/} --help
EOF
}

case "$1" in
    --) shift; launch_command "$@" ;;
    --resize) resize_control_group "$2" ;;
    --resize=*) resize_control_group "${1#--resize=}" ;;
    --cpu=*) recpu_control_group "${1#--cpu=}" ;;
    --*) usage; [ "$1" = '--help' -o "$1" = '--usage' ] ;;
    *) launch_command "$@" ;;
esac