From aae95a1015f724511662313af6e681d630faf8ec Mon Sep 17 00:00:00 2001 From: Chuck Date: Tue, 12 May 2026 14:49:34 -0400 Subject: [PATCH] refactor(api): resolve sudo/systemctl/reboot/poweroff paths at startup Use shutil.which() with safe fallbacks for the four privileged binaries instead of relying on bare names being resolved by the subprocess shell search. Resolves paths once at module load rather than per-call. Co-Authored-By: Claude Sonnet 4.6 --- web_interface/blueprints/api_v3.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/web_interface/blueprints/api_v3.py b/web_interface/blueprints/api_v3.py index 5911fd5a..cc551588 100644 --- a/web_interface/blueprints/api_v3.py +++ b/web_interface/blueprints/api_v3.py @@ -2,6 +2,7 @@ from flask import Blueprint, request, jsonify, Response, send_from_directory import json import os import re +import shutil import socket import sys import subprocess @@ -16,6 +17,11 @@ from typing import Optional, Tuple, Dict, Any, Type logger = logging.getLogger(__name__) +SUDO_BIN = shutil.which("sudo") or "/usr/bin/sudo" +SYSTEMCTL_BIN = shutil.which("systemctl") or "/usr/bin/systemctl" +REBOOT_BIN = shutil.which("reboot") or "/usr/sbin/reboot" +POWEROFF_BIN = shutil.which("poweroff") or "/usr/sbin/poweroff" + # Import new infrastructure from src.web_interface.api_helpers import success_response, error_response, validate_request_json from src.web_interface.errors import ErrorCode @@ -218,7 +224,7 @@ def _ensure_display_service_running(): if status.get('active'): status['started'] = False return status - result = _run_systemctl_command(['sudo', 'systemctl', 'start', 'ledmatrix.service']) + result = _run_systemctl_command([SUDO_BIN, SYSTEMCTL_BIN, 'start', 'ledmatrix.service']) service_status = _get_display_service_status() result['started'] = result.get('returncode') == 0 result['active'] = service_status.get('active') @@ -227,7 +233,7 @@ def _ensure_display_service_running(): def _stop_display_service(): """Stop the ledmatrix display service.""" - result = _run_systemctl_command(['sudo', 'systemctl', 'stop', 'ledmatrix.service']) + result = _run_systemctl_command([SUDO_BIN, SYSTEMCTL_BIN, 'stop', 'ledmatrix.service']) status = _get_display_service_status() result['active'] = status.get('active') result['status'] = status @@ -1716,7 +1722,7 @@ def execute_system_action(): if mode: # For on-demand modes, we would need to integrate with the display controller # For now, just start the display service - result = subprocess.run(['sudo', 'systemctl', 'start', 'ledmatrix.service'], + result = subprocess.run([SUDO_BIN, SYSTEMCTL_BIN, 'start', 'ledmatrix.service'], capture_output=True, text=True, timeout=15) return jsonify({ 'status': 'success' if result.returncode == 0 else 'error', @@ -1727,22 +1733,22 @@ def execute_system_action(): 'stderr': result.stderr }) else: - result = subprocess.run(['sudo', 'systemctl', 'start', 'ledmatrix.service'], + result = subprocess.run([SUDO_BIN, SYSTEMCTL_BIN, 'start', 'ledmatrix.service'], capture_output=True, text=True, timeout=15) elif action == 'stop_display': - result = subprocess.run(['sudo', 'systemctl', 'stop', 'ledmatrix.service'], + result = subprocess.run([SUDO_BIN, SYSTEMCTL_BIN, 'stop', 'ledmatrix.service'], capture_output=True, text=True, timeout=15) elif action == 'enable_autostart': - result = subprocess.run(['sudo', 'systemctl', 'enable', 'ledmatrix.service'], + result = subprocess.run([SUDO_BIN, SYSTEMCTL_BIN, 'enable', 'ledmatrix.service'], capture_output=True, text=True, timeout=15) elif action == 'disable_autostart': - result = subprocess.run(['sudo', 'systemctl', 'disable', 'ledmatrix.service'], + result = subprocess.run([SUDO_BIN, SYSTEMCTL_BIN, 'disable', 'ledmatrix.service'], capture_output=True, text=True, timeout=15) elif action == 'reboot_system': - result = subprocess.run(['sudo', 'reboot'], + result = subprocess.run([SUDO_BIN, REBOOT_BIN], capture_output=True, text=True, timeout=10) elif action == 'shutdown_system': - result = subprocess.run(['sudo', 'poweroff'], + result = subprocess.run([SUDO_BIN, POWEROFF_BIN], capture_output=True, text=True, timeout=10) elif action == 'git_pull': # Use PROJECT_ROOT instead of hardcoded path @@ -1824,10 +1830,10 @@ def execute_system_action(): 'stderr': result.stderr }) elif action == 'restart_display_service': - result = subprocess.run(['sudo', 'systemctl', 'restart', 'ledmatrix.service'], + result = subprocess.run([SUDO_BIN, SYSTEMCTL_BIN, 'restart', 'ledmatrix.service'], capture_output=True, text=True, timeout=15) elif action == 'restart_web_service': - result = subprocess.run(['sudo', 'systemctl', 'restart', 'ledmatrix-web.service'], + result = subprocess.run([SUDO_BIN, SYSTEMCTL_BIN, 'restart', 'ledmatrix-web.service'], capture_output=True, text=True, timeout=15) else: return jsonify({'status': 'error', 'message': f'Unknown action: {action}'}), 400