josie / alder-tools

#!/bin/sh
# aldermon-install.sh - one-stop installer / uninstaller for aldermon.
#
# Flow: RECON (checks, nothing changed) -> printed plan -> confirm ->
# ONE root escalation (doas/sudo) -> per-component install with status
# lines -> verify.
#
# Install components (each checked first; skipped when already right, except
# watts + the optional VID answer):
#   [1/4] binary+helper  install if missing or older than the built
#                        version; identical/newer left alone (--clean forces).
#                        The helper FILE is inert without its capability.
#   [2/4] /etc config    installed only if missing (system-conf edits are
#                        never clobbered unless --clean).
#   [3/4] watts          powercap udev rule + RUN chmod helper (energy_uj
#                        is 0400 root-only and powercap has no devnode).
#                        Always done - the TUI power panel needs it.
#   [4/4] VID (optional) asked at the root stage (y/n): msr udev rule
#                        (nodes 0440 adm) + cap_sys_rawio+ep on aldermon-msr
#                        only. open /dev/cpu/N/msr takes TWO gates: DAC
#                        file-mode first (setcap alone gets EACCES - the cap
#                        is not CAP_DAC_OVERRIDE), then msr_open()'s
#                        capable(CAP_SYS_RAWIO) (chmod alone gets EPERM).
#                        Declining leaves VID showing "no msr access";
#                        vCore/temps/freqs stay fully unprivileged either
#                        way.
# Install also removes pre-rename leftovers (60-adlermon-powercap.rules,
# sbin/adlermon-powercap-chmod) and superseded installer scripts.
#
# Uninstall removes what install created (binaries, capability, udev rules,
# RUN helper) and restores the device nodes' stock permissions, so recon
# reports everything missing again. The /etc config is KEPT (your edits)
# unless --purge.
#
# usage: aldermon-install.sh [MODE|OPTION]
#   (default)    recon -> plan -> confirm -> install
#   --clean, -c  force-reinstall every component (resets /etc config to stock)
#   --vid        plan VID as ON; no prompt
#   --no-vid     plan VID as SKIP; no prompt
#   --setup      privileged parts only (rules, setcap) for staged/package
#                installs; still plan+confirm
#   --uninstall  remove binaries, capability, rules, RUN helper; restore node
#                perms (keeps config)
#   --purge      uninstall AND delete the /etc config
#   --del-adm    on uninstall, also remove the original user from group adm
#                (default: ask at the root stage)
#   --keep-adm   on uninstall, leave the adm group alone; don't ask
# Install adds the original user to group adm if missing; activate it with a
# re-login or `newgrp adm` (current shell only).
#   --check      status report; changes nothing
#   --yes, -y    accept the plan without prompting
#   --help, -h   this text
#
# env: PREFIX (default /usr/local), SYSCONF (/etc), DATADIR (/usr/share)

set -eu

SRCDIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
PREFIX=${PREFIX:-/usr/local}
SYSCONF=${SYSCONF:-/etc}
DATADIR=${DATADIR:-/usr/share}

BIN=$PREFIX/bin/aldermon
MSRBIN=$PREFIX/bin/aldermon-msr
CONF=$SYSCONF/aldermon/aldermon.conf
STOCK_CONF=$SRCDIR/aldermon.conf
HELPER=$PREFIX/sbin/aldermon-powercap-chmod
RULE=$SYSCONF/udev/rules.d/60-aldermon-powercap.rules
MSRRULE=$SYSCONF/udev/rules.d/60-aldermon-msr.rules
REL=$SRCDIR/target/release

_seen_section=0
section() { if [ "$_seen_section" = 1 ]; then printf '\n'; fi; printf '== %s ==\n' "$1"; _seen_section=1; }
item() { printf '  %s\n' "$*"; }
sub() { printf '    %s\n' "$*"; }
say() { printf '%s\n' "$*"; }
die() { printf 'aldermon-install: error: %s\n' "$*" >&2; exit 1; }

usage() { sed -n '/^# usage:/,/^# env:/p' "$0" | sed 's/^# //;s/^#$//'; }

ver_older() { # a b -> true if a is a strictly older dotted version
    [ "$1" != "$2" ] && [ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)" = "$1" ]
}

installed_ver() { # echoes "" | version | 0.0.0 (pre-version build)
    [ -x "$BIN" ] || return 0
    _v=$("$BIN" --version 2>/dev/null | sed -n 's/^aldermon //p') || true
    echo "${_v:-0.0.0}"
}

src_ver() { # version the tree builds (from binary if built, else Cargo.toml)
    if [ -x "$REL/aldermon" ]; then
        "$REL/aldermon" --version | sed -n 's/^aldermon //p'
    else
        sed -n 's/^version = "\([^"]*\)"/\1/p' "$SRCDIR/Cargo.toml" 2>/dev/null || echo ""
    fi
}

helper_state() { # "missing" | "installed, no capability" | "installed + capable"
    if [ ! -x "$MSRBIN" ]; then echo missing
    elif getcap "$MSRBIN" 2>/dev/null | grep -q cap_sys_rawio; then echo "installed + capable"
    else echo "installed, no capability"; fi
}

# echoes the [1/4] gate action for FORCE INST SRC
bin_action() {
    if [ "$1" = clean ]; then echo "install (forced)"
    elif [ -z "$2" ]; then echo "install (missing)"
    elif ver_older "$2" "$3"; then echo "upgrade $2 -> $3"
    elif [ "$2" = "$3" ]; then echo "skip (up to date)"
    else echo "keep (installed $2 newer than source $3)"
    fi
}

write_if_changed() { # FILE MODE, content on stdin
    _f=$1 _m=$2 _t="$_f.tmp-aldermon-install"
    install -d "$(dirname "$_f")"
    cat >"$_t"
    if [ -f "$_f" ] && cmp -s "$_t" "$_f"; then
        sub "kept (unchanged): $_f"
        rm -f "$_t"
    else
        install -D -m "$_m" "$_t" "$_f"
        sub "installed: $_f"
        rm -f "$_t"
    fi
}

write_powercap() { # watts: RUN-chmod helper + rule, applied now
    install -d "$(dirname "$HELPER")"
    cat <<EOF | write_if_changed "$HELPER" 755
#!/bin/sh
for d in /sys/class/powercap/intel-rapl*; do
    [ -e "\$d/energy_uj" ] && chgrp adm "\$d/energy_uj" && chmod 0440 "\$d/energy_uj"
done
exit 0
EOF
    printf '%s\n' 'SUBSYSTEM=="powercap", ACTION=="add", RUN+="'$HELPER'"' |
        write_if_changed "$RULE" 644
    "$HELPER" || sub "warning: powercap chmod failed (watts may stay unreadable)"
    udevadm control --reload-rules
    udevadm trigger --subsystem-match=powercap
    sub "udev reloaded + powercap triggered"
    clean_legacy
}

write_msr_rule() { # VID gate 1: msr IS a devnode -> plain MODE/GROUP
    printf '%s\n' 'KERNEL=="msr[0-9]*", SUBSYSTEM=="msr", MODE="0440", GROUP="adm"' |
        write_if_changed "$MSRRULE" 644
    udevadm control --reload-rules # required: write_powercap reloaded BEFORE this file existed
    udevadm trigger --subsystem-match=msr
    sub "udev msr nodes triggered (0440 adm)"
}

# adm group membership: watts (energy_uj 0440 adm) and VID msr nodes both
# need it. Adds USER if missing so a fresh install works after one re-login.
ensure_adm() { # USER
    _user=${1:-}
    [ -n "$_user" ] && [ "$_user" != root ] || return 0
    if ! getent group adm >/dev/null 2>&1; then
        sub "warning: no group adm on this system; grant node access to $_user's group instead"
    elif id -nG "$_user" 2>/dev/null | tr ' ' '\n' | grep -qx adm; then
        sub "user $_user: already in group adm"
    elif usermod -aG adm "$_user"; then
        sub "added $_user to group adm - re-login (or 'newgrp adm') to activate"
    else
        sub "warning: could not add $_user to adm; do it manually: doas usermod -aG adm $_user"
    fi
}

# Pre-rename (adlermon) and superseded installer leftovers - the old rule
# still RUNs its old chmod helper, so remove the pair once ours is in.
clean_legacy() {
    for _l in "$SYSCONF/udev/rules.d/60-adlermon-powercap.rules" \
              "$PREFIX/sbin/adlermon-powercap-chmod" \
              "$DATADIR/aldermon/powercap-pl-rules.sh" \
              "$DATADIR/aldermon/aldermon-setup.sh"; do
        if [ -e "$_l" ]; then
            rm -f "$_l"
            sub "removed legacy leftover: $_l"
        fi
    done
}

setcap_helper() { # FORCE - VID gate 2: capability on the minimal helper only
    [ -x "$MSRBIN" ] || die "$MSRBIN not found - install the binary first (or use 'aldermon --vid' under root)"
    if [ "$1" != clean ] && getcap "$MSRBIN" 2>/dev/null | grep -q 'cap_sys_rawio'; then
        sub "kept (already capable): $MSRBIN"
    else
        setcap cap_sys_rawio+ep "$MSRBIN"
        sub "setcap cap_sys_rawio+ep: $MSRBIN"
    fi
}

vid_choice() { # root side: ask Y/n unless pre-answered
    case "$VIDMODE" in
        on) return 0 ;;
        off) sub "skipped (declined): VID stays unreadable - the TUI shows 'VID n/a (no msr access)'. Re-run with --vid to add it."; return 1 ;;
    esac
    printf 'enable VID extras (msr rule + helper setcap)? [Y/n] '
    read -r _ans || _ans=y
    case "$_ans" in
        [nN]*) sub "skipped (declined): VID stays unreadable - the TUI shows 'VID n/a (no msr access)'. Re-run with --vid to add it."; return 1 ;;
        *) return 0 ;;
    esac
}

verify() { # ORIGUSER VIDENABLED
    _u=${1:-$(id -un)} _vid=${2:-yes}
    section verify
    if [ -x "$BIN" ]; then item "binary: $("$BIN" --version 2>/dev/null || echo 'installed, pre-version')"; fi
    _c=$(getcap "$MSRBIN" 2>/dev/null || true)
    if [ "$_vid" = yes ]; then
        item "helper caps: ${_c:-NONE (VID stays unreadable - run --setup)}"
    else
        item "VID: not wanted this run - TUI shows 'VID n/a (no msr access)'"
        if [ -f "$MSRRULE" ]; then
            item "note: an msr rule from a previous install is still present (left alone; VID still off until the helper is setcap'd)"
        fi
    fi
    if [ -e /dev/cpu/0/msr ]; then
        item "msr nodes: $(ls -l /dev/cpu/0/msr | awk '{print $1, $3, $4}')"
    else
        item "warning: /dev/cpu/0/msr absent - msr module not loaded"
    fi
    _e=$(ls /sys/class/powercap/intel-rapl:0/energy_uj 2>/dev/null || true)
    if [ -n "$_e" ]; then item "energy_uj: $(ls -l "$_e" | awk '{print $1, $3, $4}')"; fi
    if [ -f "$CONF" ]; then item "config: $CONF present"; else item "config: $CONF MISSING"; fi
    if id -nG "$_u" 2>/dev/null | tr ' ' '\n' | grep -qx adm; then
        item "user $_u: in group adm (existing shells need re-login or 'newgrp adm')"
    else
        item "user $_u: NOT in group adm"
    fi
}

recon() { # install/setup: status + planned actions, BEFORE any root action
    _src=$(src_ver) _inst=$(installed_ver)
    section recon
    item "installed: ${_inst:-none} | source: ${_src:-unknown}"
    item "helper: $(helper_state)"
    item "rules: powercap $([ -f "$RULE" ] && echo present || echo missing), msr $([ -f "$MSRRULE" ] && echo present || echo missing)"
    item "config: $CONF $([ -f "$CONF" ] && echo present || echo missing)"
    _e=$(ls -l /sys/class/powercap/intel-rapl:0/energy_uj 2>/dev/null | awk '{print $1, $3, $4}')
    item "watts source: energy_uj ${_e:-unreadable}"
    item "planned actions:"
    if [ "$MODE" != setup ]; then
        _act=$(bin_action "$FORCE" "$_inst" "$_src")
        if [ "$MODE" = install ] && [ ! -x "$REL/aldermon" ]; then _act="$_act (after release build)"; fi
        sub "[1/4] binary+helper: $_act"
        if [ "$FORCE" = clean ]; then sub "[2/4] config: reset $CONF to stock"
        elif [ -f "$CONF" ]; then sub "[2/4] config: keep existing $CONF"
        else sub "[2/4] config: install stock $CONF"; fi
    fi
    sub "[3/4] watts: ensure powercap rule + $HELPER (group adm; needs root)"
    _u=$(id -un)
    if id -nG "$_u" 2>/dev/null | tr ' ' '\n' | grep -qx adm; then
        sub "groups: $_u already in adm"
    else
        sub "groups: add $_u to adm (needs root; re-login or 'newgrp adm' after)"
    fi
    case "$VIDMODE" in
        off) sub "[4/4] VID: SKIP (--no-vid)" ;;
        on) sub "[4/4] VID: msr rule + setcap helper (needs root)" ;;
        *) sub "[4/4] VID: OPTIONAL - asked after the root prompt; msr rule 0440 adm + cap_sys_rawio+ep on aldermon-msr only. Decline keeps 'VID n/a'; vCore/temps/freqs need no privileges either way." ;;
    esac
}

recon_uninstall() {
    section recon
    item "binary: $([ -x "$BIN" ] && echo "$BIN ($("$BIN" --version 2>/dev/null || echo 'pre-version'))" || echo "not installed")"
    item "helper: $(helper_state)"
    item "rules: powercap $([ -f "$RULE" ] && echo present || echo missing), msr $([ -f "$MSRRULE" ] && echo present || echo missing)"
    item "config: $CONF $([ -f "$CONF" ] && echo present || echo missing)"
    item "planned actions:"
    if [ "$PURGE" = yes ]; then
        sub "remove binaries, capability, both udev rules, RUN helper, installer copy"
        sub "restore /dev/cpu/*/msr to 0600 root:root and energy_uj to 0400 root:root"
        sub "remove config $CONF (--purge)"
    else
        sub "remove binaries, capability, both udev rules, RUN helper, installer copy"
        sub "restore /dev/cpu/*/msr to 0600 root:root and energy_uj to 0400 root:root"
        sub "keep config $CONF (add --purge to remove)"
    fi
    case "$ADMREMOVE" in
        yes) sub "remove $(id -un) from group adm" ;;
        no) sub "leave group adm alone" ;;
        *) sub "ask at the root stage whether to remove $(id -un) from group adm" ;;
    esac
}

confirm_or_go() { # plan acceptance before escalating
    [ "$ASSUME" = yes ] && return 0
    printf 'proceed? [Y/n] '
    read -r _r || die "no tty for confirmation - use --yes"
    case "$_r" in
        [nN]*) say "aborted, nothing changed"; exit 0 ;;
    esac
}

root_install() { # FORCE VIDMODE PURGE ORIGUSER PREFIX SYSCONF DATADIR
    FORCE=$1; VIDMODE=$2; _orig=$4; PREFIX=$5; SYSCONF=$6; DATADIR=$7
    BIN=$PREFIX/bin/aldermon; MSRBIN=$PREFIX/bin/aldermon-msr
    CONF=$SYSCONF/aldermon/aldermon.conf; STOCK_CONF=$SRCDIR/aldermon.conf
    HELPER=$PREFIX/sbin/aldermon-powercap-chmod
    RULE=$SYSCONF/udev/rules.d/60-aldermon-powercap.rules
    MSRRULE=$SYSCONF/udev/rules.d/60-aldermon-msr.rules

    [ -x "$REL/aldermon" ] || die "no release build in $SRCDIR - run the script unprivileged (it builds) or cargo build --release first"
    SRCVER=$(src_ver)
    INSTVER=$(installed_ver)

    section install
    item "[1/4] binary (installed: ${INSTVER:-none}, source: $SRCVER)"
    _act=$(bin_action "$FORCE" "$INSTVER" "$SRCVER")
    case $_act in
        install* | upgrade*)
            install -Dm755 "$REL/aldermon" "$BIN"
            install -Dm755 "$REL/aldermon-msr" "$MSRBIN"
            sub "installed: $BIN + $MSRBIN" ;;
        skip*)
            if [ ! -x "$MSRBIN" ]; then
                install -Dm755 "$REL/aldermon-msr" "$MSRBIN"
                sub "skipped binary (up to date); installed missing helper"
            else
                sub "skipped: up to date (use --clean to force)"
            fi ;;
        keep*) sub "$_act" ;;
    esac

    item "[2/4] config"
    if [ "$FORCE" = clean ] || [ ! -e "$CONF" ]; then
        [ -f "$STOCK_CONF" ] || die "stock config $STOCK_CONF not found (installed copy? use --setup)"
        install -Dm644 "$STOCK_CONF" "$CONF"
        sub "installed stock: $CONF"
    else
        sub "kept existing: $CONF (--clean resets to stock)"
    fi

    item "[3/4] watts (powercap)"
    write_powercap

    item "[4/4] VID (optional)"
    _vid=no
    if vid_choice; then
        _vid=yes
        write_msr_rule
        setcap_helper "$FORCE"
    fi

    item "group membership"
    ensure_adm "$_orig"

    verify "$_orig" "$_vid"
}

root_setup() { # FORCE VIDMODE PURGE ORIGUSER PREFIX SYSCONF DATADIR
    FORCE=$1; VIDMODE=$2; _orig=$4; PREFIX=$5; SYSCONF=$6; DATADIR=$7
    BIN=$PREFIX/bin/aldermon; MSRBIN=$PREFIX/bin/aldermon-msr
    CONF=$SYSCONF/aldermon/aldermon.conf
    HELPER=$PREFIX/sbin/aldermon-powercap-chmod
    RULE=$SYSCONF/udev/rules.d/60-aldermon-powercap.rules
    MSRRULE=$SYSCONF/udev/rules.d/60-aldermon-msr.rules
    section install
    item "setup: watts (powercap)"
    write_powercap
    item "setup: VID (optional)"
    _vid=no
    if vid_choice; then
        _vid=yes
        write_msr_rule
        setcap_helper "$FORCE"
    fi
    item "group membership"
    ensure_adm "$_orig"
    verify "$_orig" "$_vid"
}

root_uninstall() { # FORCE VIDMODE PURGE ORIGUSER PREFIX SYSCONF DATADIR ADMREMOVE
    PURGE=$3; _orig=$4; PREFIX=$5; SYSCONF=$6; DATADIR=$7; ADMREMOVE=$8
    BIN=$PREFIX/bin/aldermon; MSRBIN=$PREFIX/bin/aldermon-msr
    CONF=$SYSCONF/aldermon/aldermon.conf
    HELPER=$PREFIX/sbin/aldermon-powercap-chmod
    RULE=$SYSCONF/udev/rules.d/60-aldermon-powercap.rules
    MSRRULE=$SYSCONF/udev/rules.d/60-aldermon-msr.rules

    section uninstall
    if [ -x "$MSRBIN" ]; then
        setcap -r "$MSRBIN" 2>/dev/null && item "dropped capability: $MSRBIN" || item "capability already absent"
    fi
    for _f in "$BIN" "$MSRBIN" "$HELPER" "$RULE" "$MSRRULE" "$DATADIR/aldermon/aldermon-install.sh"; do
        if [ -e "$_f" ]; then rm -f "$_f"; item "removed: $_f"; fi
    done
    clean_legacy
    # Restore stock device perms so recon reports them unset again.
    if [ -e /dev/cpu/0/msr ]; then
        chgrp root /dev/cpu/*/msr 2>/dev/null || true
        chmod 0600 /dev/cpu/*/msr 2>/dev/null || true
        item "restored /dev/cpu/*/msr -> 0600 root:root"
    fi
    _e=$(ls /sys/class/powercap/intel-rapl:0/energy_uj 2>/dev/null || true)
    if [ -n "$_e" ]; then
        chgrp root "$_e" 2>/dev/null || true
        chmod 0400 "$_e" 2>/dev/null || true
        item "restored energy_uj -> 0400 root:root"
    fi
    udevadm control --reload-rules
    item "udev rules reloaded"
    if [ "$PURGE" = yes ]; then
        if [ -e "$CONF" ]; then rm -f "$CONF"; item "removed config: $CONF"; fi
    else
        item "kept config: $CONF (--purge removes it)"
    fi
    rmdir -p --ignore-fail-on-non-empty "$DATADIR/aldermon" 2>/dev/null || true
    verify_uninstall "$_orig" "$ADMREMOVE"
}

verify_uninstall() { # ORIGUSER ADMREMOVE
    _u=${1:-$(id -un)} _adm=${2:-no}
    section verify
    item "binary: $([ -e "$BIN" ] && echo STILL PRESENT || echo gone)"
    item "helper: $([ -e "$MSRBIN" ] && echo STILL PRESENT || echo gone)"
    item "powercap rule: $([ -e "$RULE" ] && echo STILL PRESENT || echo gone)"
    item "msr rule: $([ -e "$MSRRULE" ] && echo STILL PRESENT || echo gone)"
    if [ -e /dev/cpu/0/msr ]; then
        item "msr node: $(ls -l /dev/cpu/0/msr | awk '{print $1, $3, $4}') (stock 0600 root root)"
    fi
    _e=$(ls /sys/class/powercap/intel-rapl:0/energy_uj 2>/dev/null || true)
    if [ -n "$_e" ]; then item "energy_uj: $(ls -l "$_e" | awk '{print $1, $3, $4}')"; fi
    item "config: $CONF $([ -e "$CONF" ] && echo present || echo gone)"
    if id -nG "$_u" 2>/dev/null | tr ' ' '\n' | grep -qx adm; then
        _do_adm=no
        case "$_adm" in
            yes) _do_adm=yes ;;
            no) item "user $_u: still in group adm (use --del-adm to remove; adm also grants log access)" ;;
            *)
                printf 'remove %s from group adm? [y/N] ' "$_u"
                read -r _r || _r=n
                case "$_r" in [yY]*) _do_adm=yes ;; esac
                ;;
        esac
        if [ "$_do_adm" = yes ]; then
            if gpasswd -d "$_u" adm >/dev/null 2>&1; then
                item "removed $_u from group adm (re-login for it to take effect)"
            else
                item "could not remove $_u from adm; do it manually: doas gpasswd -d $_u adm"
            fi
        elif [ "$_adm" = ask ]; then
            item "kept $_u in group adm (adm also grants log access)"
        fi
    fi
}

escalate() { # INTERNALMODE
    _mode=$1 _u=$(id -un)
    if [ "$(id -u)" -eq 0 ]; then
        "$0" "$_mode" "$FORCE" "$VIDMODE" "$PURGE" "$_u" "$PREFIX" "$SYSCONF" "$DATADIR" "$ADMREMOVE"
    else
        PRIV=$(command -v doas 2>/dev/null || command -v sudo 2>/dev/null || true)
        [ -n "$PRIV" ] || die "no doas or sudo found - run the privileged steps as root"
        say "requesting root via $PRIV"
        exec "$PRIV" "$0" "$_mode" "$FORCE" "$VIDMODE" "$PURGE" "$_u" "$PREFIX" "$SYSCONF" "$DATADIR" "$ADMREMOVE"
    fi
}

check_status() {
    section status
    if [ -x "$BIN" ]; then
        item "binary: $("$BIN" --version 2>/dev/null || echo 'installed, pre-version (<= 2026-09)')"
        if [ -x "$REL/aldermon" ]; then
            _s=$("$REL/aldermon" --version | sed -n 's/^aldermon //p')
            _i=$("$BIN" --version 2>/dev/null | sed -n 's/^aldermon //p')
            [ -n "$_i" ] || _i=0.0.0
            if ver_older "$_i" "$_s"; then item "update available: source builds $_s"
            else item "source build: $_s $([ "$_i" = "$_s" ] && echo '(match)' || echo '(source is older)')"; fi
        fi
    else
        item "binary: NOT INSTALLED ($BIN)"
    fi
    item "helper: $(helper_state)"
    item "powercap rule: $([ -f "$RULE" ] && echo present || echo MISSING)"
    item "msr rule: $([ -f "$MSRRULE" ] && echo present || echo MISSING)"
    if [ -e /dev/cpu/0/msr ]; then
        item "msr node: $(ls -l /dev/cpu/0/msr | awk '{print $1, $3, $4}') (needs 0440 adm + helper cap)"
    fi
    item "config: $CONF $([ -f "$CONF" ] && echo present || echo MISSING)"
    if id -nG "$(id -un)" | tr ' ' '\n' | grep -qx adm; then
        item "user $(id -un): in adm"
    else
        item "user $(id -un): NOT in adm"
    fi
}

MODE=install
FORCE=plain
VIDMODE=ask
PURGE=no
ADMREMOVE=ask
ASSUME=no
while [ $# -gt 0 ]; do
    case $1 in
        -h | --help) usage; exit 0 ;;
        --check) MODE=check ;;
        --setup) MODE=setup ;;
        --uninstall) MODE=uninstall ;;
        --purge) MODE=uninstall; PURGE=yes ;;
        --clean | -c) FORCE=clean ;;
        --yes | -y) ASSUME=yes ;;
        --no-vid) VIDMODE=off ;;
        --vid) VIDMODE=on ;;
        --del-adm) ADMREMOVE=yes ;;
        --keep-adm) ADMREMOVE=no ;;
        _root) shift; root_install "$@"; exit 0 ;;
        _setup) shift; root_setup "$@"; exit 0 ;;
        _unroot) shift; root_uninstall "$@"; exit 0 ;;
        *) die "unknown argument: $1 (see --help)" ;;
    esac
    shift
done

case $MODE in
    check) check_status ;;
    uninstall)
        recon_uninstall
        confirm_or_go
        escalate _unroot
        ;;
    setup)
        recon
        confirm_or_go
        escalate _setup
        ;;
    install)
        [ -f "$SRCDIR/Cargo.toml" ] || die "$SRCDIR is not a source tree - run from the checkout, or use --setup/--check"
        recon
        confirm_or_go
        if [ ! -x "$REL/aldermon" ]; then
            command -v cargo >/dev/null || die "cargo not found and no release build present"
            say "building release binaries (as $(id -un))"
            (cd "$SRCDIR" && cargo build --release)
        fi
        escalate _root
        ;;
esac