#!/usr/bin/sh
# dc_unpause — resume the local DC (distributed computing) clients.
#
# Called by idle_detect when the user becomes idle (see idle_command
# in idle_detect.conf). Handles BOINC and Folding@home independently:
# each is attempted only if present, and the absence or failure of one
# never prevents the other from being actioned.
#
# NOTE: This script is replaced on every package install and every
# install.sh run. To customize permanently:
#   1. Copy this file to ~/.local/bin/
#   2. Edit the copy.
#   3. Point idle_command in ~/.config/idle_detect.conf at the copy
#      (use the full path — ~/ does not expand in the config file).
#   4. systemctl --user restart dc_idle_detection
#
# Environment overrides (e.g. via a user-service override at
# ~/.config/systemd/user/dc_idle_detection.service.d/override.conf):
#   BOINC_DATA_DIR   BOINC data directory, skipping the probe below.
#   DC_PAUSE_BOINC   Set to 0 to leave BOINC alone.
#   DC_PAUSE_FAH     Set to 0 to leave Folding@home alone.
#   DC_FAH_CLIENT    auto (default), v7, or v8 — force a client generation.
#
# Folding@home has two incompatible control surfaces:
#   v7  FAHClient --send-pause / --send-unpause
#   v8  No CLI ships with the client at all. Control is a WebSocket API on
#       127.0.0.1:7396, driven here by the bundled dc_fah_v8 helper (Python 3
#       standard library only). It stays on this machine and never involves
#       the hosted web control.
# The official 'fahctl' is deliberately not used: it is not installed by the v8
# package, it needs the third-party websocket-client module, and against client
# 8.1.18 it sends a message shape the client ignores while still exiting 0.
# dc_fah_v8 re-reads the client state to confirm the change instead.
#
# We drive pause/resume explicitly rather than relying on either client's
# own idle detection, which is exactly the unreliability this project exists
# to work around.

# Deliberately no 'set -e': BOINC and Folding@home are independent, and an
# error talking to one must not stop us resuming the other.
set -u

acted=0
failed=0

# --- BOINC ------------------------------------------------------------
if [ "${DC_PAUSE_BOINC:-1}" != "0" ]; then
    # Honor a user-provided data directory first; otherwise probe common paths.
    boinc_dir="${BOINC_DATA_DIR:-}"

    if [ -z "$boinc_dir" ]; then
        for candidate in \
            /var/lib/boinc \
            /var/lib/boinc-client \
            /var/snap/boinc/common \
            "$HOME/BOINC" \
            "$HOME/.BOINC" \
            "$HOME/.var/app/edu.berkeley.BOINC/data"
        do
            if [ -r "$candidate/gui_rpc_auth.cfg" ]; then
                boinc_dir="$candidate"
                break
            fi
        done
    fi

    if [ -n "$boinc_dir" ] && [ -r "$boinc_dir/gui_rpc_auth.cfg" ] \
       && command -v boinccmd >/dev/null 2>&1; then
        password=$(cat "$boinc_dir/gui_rpc_auth.cfg")

        if boinccmd --host localhost --passwd "$password" --set_run_mode always; then
            echo 'Set BOINC client to run mode "always".'
            acted=1
        else
            echo "dc_unpause: boinccmd failed. Is BOINC running?" >&2
            failed=1
        fi
    elif command -v boinccmd >/dev/null 2>&1; then
        # BOINC tooling is installed but its RPC password is not readable, so we
        # cannot control it. Say so rather than skipping quietly: silently
        # leaving compute running is worse than a log line.
        echo "dc_unpause: BOINC is installed but no readable gui_rpc_auth.cfg was" >&2
        echo "  found. Add your user to the 'boinc' group, or set BOINC_DATA_DIR." >&2
        failed=1
    fi
fi

# --- Folding@home -----------------------------------------------------
if [ "${DC_PAUSE_FAH:-1}" != "0" ]; then
    # idle_detect runs this via system() in a detached thread and discards the
    # exit code, so a call that never returns would leak a thread on every
    # activity transition. Bound it when timeout(1) is available.
    if command -v timeout >/dev/null 2>&1; then
        fah_guard="timeout 20"
    else
        fah_guard=""
    fi

    # Locate the v8 helper: alongside this script first, then PATH.
    fah_v8=""
    if [ -x "$(dirname "$0")/dc_fah_v8" ]; then
        fah_v8="$(dirname "$0")/dc_fah_v8"
    elif command -v dc_fah_v8 >/dev/null 2>&1; then
        fah_v8=dc_fah_v8
    fi

    # Pick a generation. The v8 package ships no CLI, so the presence of the
    # fah-client daemon is the signal. A v7 FAHClient binary can linger after an
    # upgrade, so v8 wins in auto mode.
    fah_gen="${DC_FAH_CLIENT:-auto}"
    if [ "$fah_gen" = "auto" ]; then
        if command -v fah-client >/dev/null 2>&1; then
            fah_gen=v8
        elif command -v FAHClient >/dev/null 2>&1; then
            fah_gen=v7
        else
            fah_gen=none
        fi
    fi

    case "$fah_gen" in
        v7)
            if $fah_guard FAHClient --send-unpause >/dev/null 2>&1; then
                echo 'Resumed Folding@home (v7).'
                acted=1
            else
                echo "dc_unpause: FAHClient --send-unpause failed. Is FAHClient running?" >&2
                failed=1
            fi
            ;;
        v8)
            if [ -z "$fah_v8" ]; then
                echo "dc_unpause: Folding@home v8 is installed but dc_fah_v8 was not" >&2
                echo "  found next to this script or in PATH." >&2
                failed=1
            elif $fah_guard "$fah_v8" unpause; then
                acted=1
            else
                failed=1
            fi
            ;;
        none)
            : # No Folding@home client installed; nothing to do.
            ;;
        *)
            echo "dc_unpause: DC_FAH_CLIENT='$fah_gen' is not one of auto, v7, v8." >&2
            failed=1
            ;;
    esac
fi

# --- Result -----------------------------------------------------------
if [ "$acted" -eq 0 ] && [ "$failed" -eq 0 ]; then
    echo "dc_unpause: found no supported DC client (BOINC or Folding@home)." >&2
    echo "  Set BOINC_DATA_DIR, or copy this script to ~/.local/bin/ and" >&2
    echo "  edit it — see the header." >&2
    exit 1
fi

[ "$failed" -eq 0 ] || exit 1
exit 0
