105 lines
4.3 KiB
Python
105 lines
4.3 KiB
Python
import os, logging, subprocess, threading
|
|
DEBUG = os.environ.get('DEBUG')
|
|
logging.basicConfig(
|
|
format='[%(asctime)s] %(levelname)s %(module)s/%(funcName)s - %(message)s',
|
|
level=logging.DEBUG if DEBUG else logging.INFO)
|
|
|
|
from flask import Flask, request
|
|
|
|
AUTO_STOP_TIMEOUT_SECONDS = 3600 # 1 hour
|
|
LAST_CAST_ADDRESS = None
|
|
|
|
app = Flask(__name__)
|
|
auto_stop_timer = None # Timer for automatic VNC stop
|
|
|
|
@app.route('/cast', methods=['POST'])
|
|
def cast_spell():
|
|
ip_address = request.form.get('ip_address')
|
|
logging.info(f"Received POST request on /cast. Requested ip_address: {ip_address}")
|
|
|
|
if not ip_address:
|
|
logging.warning(f"Invalid or missing ip_address parameter: {ip_address}")
|
|
return f"Invalid or missing 'ip_address' parameter.", 400
|
|
|
|
global auto_stop_timer, LAST_CAST_ADDRESS
|
|
|
|
logging.info(f"Casting to {ip_address}.")
|
|
kill_vnc() # Kill any existing VNC session first
|
|
cast_machine(ip_address)
|
|
LAST_CAST_ADDRESS = ip_address
|
|
# Restart auto-stop timer
|
|
if auto_stop_timer is not None and auto_stop_timer.is_alive():
|
|
auto_stop_timer.cancel()
|
|
logging.info("Cancelled previous auto-stop timer.")
|
|
auto_stop_timer = threading.Timer(AUTO_STOP_TIMEOUT_SECONDS, _auto_stop_vnc)
|
|
auto_stop_timer.start()
|
|
logging.info(f"Scheduled auto-stop for VNC in {AUTO_STOP_TIMEOUT_SECONDS} seconds.")
|
|
return f"Successfully cast to {ip_address}.", 200
|
|
|
|
@app.route('/stop', methods=['POST'])
|
|
def stop_cast():
|
|
global auto_stop_timer, LAST_CAST_ADDRESS
|
|
ip_address = request.form.get('ip_address')
|
|
logging.info(f"Received POST request on /stop for ip_address: {ip_address}")
|
|
|
|
if not ip_address:
|
|
logging.warning(f"Invalid or missing ip_address parameter: {ip_address}")
|
|
return f"Invalid or missing 'ip_address' parameter.", 400
|
|
|
|
if ip_address != LAST_CAST_ADDRESS:
|
|
logging.warning(f"Stop request for {ip_address} does not match last cast address {LAST_CAST_ADDRESS}.")
|
|
return f"Stop request for {ip_address} does not match last cast address.", 403
|
|
|
|
if auto_stop_timer is not None and auto_stop_timer.is_alive():
|
|
auto_stop_timer.cancel()
|
|
auto_stop_timer = None
|
|
logging.info("Manual stop: auto-stop timer cancelled.")
|
|
|
|
kill_vnc()
|
|
LAST_CAST_ADDRESS = None
|
|
return "Attempted to stop VNC viewers.", 200
|
|
|
|
def _auto_stop_vnc():
|
|
"""Called by the timer to automatically stop VNC."""
|
|
global auto_stop_timer
|
|
logging.info("Auto-stopping VNC viewers due to inactivity timer.")
|
|
kill_vnc()
|
|
auto_stop_timer = None # Clear the timer reference
|
|
|
|
def cast_machine(ip_address):
|
|
"""Executes the xtightvncviewer command for a given IP address."""
|
|
command = f"DISPLAY=:1 xtightvncviewer -viewonly -fullscreen {ip_address}"
|
|
try:
|
|
logging.info(f"Launching command: {command}")
|
|
# Use Popen to run the command in the background (non-blocking)
|
|
subprocess.Popen(command, shell=True)
|
|
logging.info(f"Command '{command}' launched successfully.")
|
|
except Exception as e: # Catch other potential errors during Popen
|
|
logging.error(f"An error occurred while launching VNC for {ip_address} with Popen: {e}")
|
|
|
|
def kill_vnc():
|
|
"""Executes the killall command for xtightvncviewer."""
|
|
command = "killall xtightvncviewer"
|
|
try:
|
|
logging.info(f"Executing command: {command}")
|
|
# We don't use check=True here because killall returns a non-zero exit code
|
|
# if no processes were killed, which is not necessarily an error in this context.
|
|
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
|
if result.returncode == 0:
|
|
logging.info("killall command executed successfully. Processes were likely terminated.")
|
|
else:
|
|
# killall returns 1 if no matching processes were found.
|
|
# Other non-zero codes might indicate other errors.
|
|
logging.warning(f"killall command finished. Exit code: {result.returncode}. Stderr: {result.stderr.strip()}")
|
|
if "no process found" in result.stderr.lower():
|
|
logging.info("No xtightvncviewer processes were found to kill.")
|
|
|
|
except Exception as e:
|
|
logging.error(f"An unexpected error occurred while trying to run killall: {e}")
|
|
|
|
def main():
|
|
app.run(debug=DEBUG, host='0.0.0.0', port=int(os.environ.get('PORT', 5000)))
|
|
|
|
if __name__ == '__main__':
|
|
main()
|