Detailed documentation for the sudo_keepalive module (src/helper/sudo_keepalive.py).
The sudo_keepalive module maintains sudo privileges throughout script execution by periodically refreshing the sudo timestamp in the background. This eliminates repeated password prompts during long-running operations in Tuxgrade.
Starts the global sudo keepalive thread.
- Root Check: Skips if already running as root (EUID=0)
- Sudo Validation: Requests sudo password once (
sudo -v) - Background Thread: Launches daemon thread executing
sudo -n trueevery 60 seconds - Automatic Cleanup: Registers handlers for atexit, SIGINT, and SIGTERM
refresh_interval(int): Seconds between refreshes (default: 60)
SystemExit: Raised when sudo validation fails
from helper import sudo_keepalive
sudo_keepalive.start()
# Execute sudo commands without password promptsIf called while already active, logs a warning but doesn't fail:
sudo_keepalive.start()
sudo_keepalive.start() # Warning: "Sudo keepalive is already running"Stops the global sudo keepalive thread.
Sets the stop event and waits for the background thread to terminate gracefully.
from helper import sudo_keepalive
sudo_keepalive.stop()Safe to call even if not running:
sudo_keepalive.stop() # No error if already stoppedChecks if the keepalive thread is currently active.
True: Keepalive thread is runningFalse: Keepalive thread is not running
from helper import sudo_keepalive
if sudo_keepalive.is_running():
print("Keepalive active")
else:
print("Keepalive not running")Internal class for direct instantiation when more control is needed.
start() -> None: Starts the keepalive threadstop() -> None: Stops the keepalive threadis_running() -> bool: Returns thread status
from helper.sudo_keepalive import SudoKeepalive
keepalive = SudoKeepalive(refresh_interval=30)
keepalive.start()
# ... operations ...
keepalive.stop()Note: The module-level functions use a global singleton instance. Direct instantiation is rarely needed.
from helper import sudo_keepalive, runner
def main():
sudo_keepalive.start()
try:
runner.run(["sudo", "dnf", "update", "-y"])
runner.run(["sudo", "dnf", "clean", "all"])
finally:
sudo_keepalive.stop()from helper import sudo_keepalive, runner
def main():
print("Starting system update...")
sudo_keepalive.start()
try:
# DNF updates
runner.run(["sudo", "dnf", "update", "-y"])
# Flatpak updates
runner.run(["sudo", "flatpak", "update", "-y"])
# NVIDIA akmods
runner.run(["sudo", "akmods", "--force"])
print("✅ Update completed successfully")
return 0
except runner.CommandError as e:
print(f"❌ Error: {e}")
return 1
except KeyboardInterrupt:
print("\n⚠️ Aborted by user")
return 130
finally:
sudo_keepalive.stop()from helper import sudo_keepalive
# Refresh every 30 seconds instead of 60
sudo_keepalive.start(refresh_interval=30)from helper import sudo_keepalive, runner
def main():
sudo_keepalive.start()
try:
update_dnf()
update_flatpak()
update_nvidia()
except KeyboardInterrupt:
print("\nUpdate cancelled")
raise SystemExit(130)
finally:
sudo_keepalive.stop()- Daemon Thread: Automatically terminates when main process exits
- Event-Based: Uses
threading.Event()for clean shutdown - Non-Blocking: Waits with timeout instead of sleep
Implementation:
def _refresh_loop(self):
while not self._stop_event.is_set():
self._refresh_sudo()
self._stop_event.wait(self.refresh_interval) # Interruptible wait- atexit: Registered during
start(), runs on normal exit - Signal Handlers: Catches SIGINT (Ctrl+C) and SIGTERM
- try-finally: Additional protection in main script
Registration:
atexit.register(self.stop)
signal.signal(signal.SIGINT, self._handle_signal)
signal.signal(signal.SIGTERM, self._handle_signal)- Minimal Privileges: Thread only executes
sudo -n true, no other commands - User Control: Initial password prompt gives user full control
- Automatic Timeout: Thread stops automatically if refresh fails
When the script is already running as root (EUID=0), the keepalive does nothing:
if os.geteuid() == 0:
logging.debug("Already running as root, sudo keepalive not needed")
returnWhy? Root doesn't need sudo, so the keepalive would be redundant.
The module uses Python's logging framework for diagnostics.
"Already running as root, sudo keepalive not needed""Sudo keepalive started (refresh every 60s)""Sudo timestamp refreshed""Stopping sudo keepalive"
"Sudo keepalive is already running""Failed to refresh sudo timestamp"
"Error refreshing sudo: {exception}""Sudo validation failed: {exception}"
import logging
logging.basicConfig(level=logging.DEBUG)
# Now keepalive debug messages will be displayed# Always use finally for cleanup
sudo_keepalive.start()
try:
# ... code ...
finally:
sudo_keepalive.stop()
# Start only once per process (singleton pattern)
sudo_keepalive.start()# No cleanup - thread keeps running!
sudo_keepalive.start()
# ... code ...
# Multiple starts (works but unnecessary)
sudo_keepalive.start()
sudo_keepalive.start() # Warning: Already runningpython3 tests/sudo_keepalive/test_basic.py
python3 tests/sudo_keepalive/test_cross_module.pyThe tests verify:
- Start/stop functionality
- Multiple sudo commands without password re-prompt
is_running()status- Cleanup on exit
- Signal handling (SIGINT, SIGTERM)
def test_sudo_commands_without_password():
sudo_keepalive.start()
# These should all succeed without password prompts
runner.run(["sudo", "true"])
runner.run(["sudo", "ls", "/root"])
sudo_keepalive.stop()- Python: 3.10+
- OS: Linux/Unix (sudo must be available)
- Dependencies: Python standard library only (subprocess, threading, signal, atexit)
- runner.md - Command execution
- cli.md - User interface
- Architecture - System design