1
0
mirror of https://github.com/tw93/Mole.git synced 2026-02-04 14:26:46 +00:00
Files
Mole/lib/sudo_manager.sh
2025-11-25 17:25:13 +08:00

152 lines
4.5 KiB
Bash

#!/bin/bash
# Sudo Session Manager
# Unified sudo authentication and keepalive management
set -euo pipefail
# Global state
MOLE_SUDO_KEEPALIVE_PID=""
MOLE_SUDO_ESTABLISHED="false"
# Start sudo keepalive background process
# Returns: PID of keepalive process
_start_sudo_keepalive() {
[[ "${MO_DEBUG:-}" == "1" ]] && echo "DEBUG: _start_sudo_keepalive: starting background process..." >&2
# Start background keepalive process with all outputs redirected
# This is critical: command substitution waits for all file descriptors to close
(
# Initial delay to let sudo cache stabilize after password entry
# This prevents immediately triggering Touch ID again
sleep 2
local retry_count=0
while true; do
if ! sudo -n -v 2> /dev/null; then
((retry_count++))
if [[ $retry_count -ge 3 ]]; then
exit 1
fi
sleep 5
continue
fi
retry_count=0
sleep 30
kill -0 "$$" 2> /dev/null || exit
done
) > /dev/null 2>&1 &
local pid=$!
[[ "${MO_DEBUG:-}" == "1" ]] && echo "DEBUG: _start_sudo_keepalive: background PID = $pid" >&2
echo $pid
}
# Stop sudo keepalive process
# Args: $1 - PID of keepalive process
_stop_sudo_keepalive() {
local pid="${1:-}"
if [[ -n "$pid" ]]; then
kill "$pid" 2> /dev/null || true
wait "$pid" 2> /dev/null || true
fi
}
# Check if sudo session is active
has_sudo_session() {
sudo -n true 2> /dev/null
}
# Request sudo access (wrapper for common.sh function)
# Args: $1 - prompt message
request_sudo() {
local prompt_msg="${1:-Admin access required}"
[[ "${MO_DEBUG:-}" == "1" ]] && echo "DEBUG: request_sudo: checking existing session..."
if has_sudo_session; then
[[ "${MO_DEBUG:-}" == "1" ]] && echo "DEBUG: request_sudo: session already exists"
return 0
fi
[[ "${MO_DEBUG:-}" == "1" ]] && echo "DEBUG: request_sudo: calling request_sudo_access from common.sh..."
# Use the robust implementation from common.sh
if request_sudo_access "$prompt_msg"; then
[[ "${MO_DEBUG:-}" == "1" ]] && echo "DEBUG: request_sudo: request_sudo_access succeeded"
return 0
else
[[ "${MO_DEBUG:-}" == "1" ]] && echo "DEBUG: request_sudo: request_sudo_access failed"
return 1
fi
}
# Ensure sudo session is established with keepalive
# Args: $1 - prompt message
ensure_sudo_session() {
local prompt="${1:-Admin access required}"
[[ "${MO_DEBUG:-}" == "1" ]] && echo "DEBUG: ensure_sudo_session called"
# Check if already established
if has_sudo_session && [[ "$MOLE_SUDO_ESTABLISHED" == "true" ]]; then
[[ "${MO_DEBUG:-}" == "1" ]] && echo "DEBUG: Sudo session already active"
return 0
fi
[[ "${MO_DEBUG:-}" == "1" ]] && echo "DEBUG: Checking for old keepalive..."
# Stop old keepalive if exists
if [[ -n "$MOLE_SUDO_KEEPALIVE_PID" ]]; then
[[ "${MO_DEBUG:-}" == "1" ]] && echo "DEBUG: Stopping old keepalive PID $MOLE_SUDO_KEEPALIVE_PID"
_stop_sudo_keepalive "$MOLE_SUDO_KEEPALIVE_PID"
MOLE_SUDO_KEEPALIVE_PID=""
fi
[[ "${MO_DEBUG:-}" == "1" ]] && echo "DEBUG: Calling request_sudo..."
# Request sudo access
if ! request_sudo "$prompt"; then
[[ "${MO_DEBUG:-}" == "1" ]] && echo "DEBUG: request_sudo failed"
MOLE_SUDO_ESTABLISHED="false"
return 1
fi
[[ "${MO_DEBUG:-}" == "1" ]] && echo "DEBUG: request_sudo succeeded, starting keepalive..."
# Start keepalive
MOLE_SUDO_KEEPALIVE_PID=$(_start_sudo_keepalive)
[[ "${MO_DEBUG:-}" == "1" ]] && echo "DEBUG: Keepalive started with PID $MOLE_SUDO_KEEPALIVE_PID"
MOLE_SUDO_ESTABLISHED="true"
return 0
}
# Stop sudo session and cleanup
stop_sudo_session() {
if [[ -n "$MOLE_SUDO_KEEPALIVE_PID" ]]; then
_stop_sudo_keepalive "$MOLE_SUDO_KEEPALIVE_PID"
MOLE_SUDO_KEEPALIVE_PID=""
fi
MOLE_SUDO_ESTABLISHED="false"
}
# Register cleanup on script exit
register_sudo_cleanup() {
trap stop_sudo_session EXIT INT TERM
}
# Check if sudo is likely needed for given operations
# Args: $@ - list of operations to check
will_need_sudo() {
local -a operations=("$@")
for op in "${operations[@]}"; do
case "$op" in
system_update | appstore_update | macos_update | firewall | touchid | rosetta | system_fix)
return 0
;;
esac
done
return 1
}