diff --git a/Python/Soket_server.py b/Python/Soket_server.py new file mode 100644 index 0000000..9fc574f --- /dev/null +++ b/Python/Soket_server.py @@ -0,0 +1,15 @@ +import socket + +HOST = 'localhost' +PORT = 12345 + +with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.connect((HOST, PORT)) + + # Se l'autenticazione è abilitata + s.sendall(b'admin\n') + s.sendall(b'password123\n') + + s.sendall(b'status\n') # Invia il comando + data = s.recv(1024) + print(f"Received: {data.decode()}") \ No newline at end of file diff --git a/Python/my_server.py b/Python/my_server.py new file mode 100644 index 0000000..a1170fc --- /dev/null +++ b/Python/my_server.py @@ -0,0 +1,525 @@ +import os +import subprocess +import threading +import socket +import socketserver +import time +import sys + +# --- CONFIGURAZIONE (DA ADATTARE ALLE TUE ESIGENZE) --- +# Queste variabili dovrebbero essere configurate in un file di configurazione separato +# o passate come argomenti al tuo script Python. + +TCPSERVER_PID_FILE = "/var/run/my_socket_server.pid" +TCPSERVER_IP = "0.0.0.0" # Ascolta su tutte le interfacce +TCPSERVER_PORT = 12345 # Porta del server socket + +TCPSERVER_USER = "admin" # Credenziali opzionali per l'autenticazione +TCPSERVER_PWD = "password123" + +# Variabili usate internamente dallo script Bash, che in Python diventano parametri o logica +RUN_FROM_TCPSERVER = False # Sarà True quando il comando è eseguito dal server socket + +# Definisci i percorsi per i comandi esterni (se non sono nel PATH di sistema) +# In un ambiente di produzione, è meglio specificare percorsi assoluti. +# Ad esempio, TR_COMMAND = "/usr/bin/tr" +TR_COMMAND = "tr" +CUT_COMMAND = "cut" +READLINK_COMMAND = "readlink" + + +# --- FUNZIONI UTILITY (PLACEHOLDERS) --- +# Queste sono le funzioni che erano richiamate dallo script Bash +# Dovrai implementarle in Python o collegarle alle tue librerie esistenti. + +def log_write(source, level, message): + """Placeholder per la funzione di logging.""" + timestamp = time.strftime("%Y-%m-%d %H:%M:%S") + print(f"[{timestamp}] [{source}] [{level.upper()}] {message}") + +def json_error(code, message): + """Placeholder per la funzione che genera un JSON di errore.""" + print(f'{{"status": "error", "code": {code}, "message": "{message}"}}') + +def json_status(*args): + """Placeholder per la funzione che genera un JSON di stato.""" + if args: + print(f'{{"status": "success", "data": "{", ".join(args)}"}}') + else: + print('{"status": "success"}') + +def message_write(level, message): + """Placeholder per la funzione che scrive un messaggio.""" + print(f"[{level.upper()}] {message}") + +def ev_open(alias, param=None): + """Placeholder per la funzione di apertura valvola.""" + log_write("socket_server", "info", f"Executing ev_open for alias: {alias}, param: {param}") + # Qui andrebbe la logica per aprire la valvola + pass + +def ev_open_in(arg2, arg3, arg4, arg5): + """Placeholder per la funzione di apertura valvola in un intervallo.""" + log_write("socket_server", "info", f"Executing ev_open_in with args: {arg2}, {arg3}, {arg4}, {arg5}") + # Qui andrebbe la logica per aprire la valvola in un intervallo + pass + +def ev_close(alias): + """Placeholder per la funzione di chiusura valvola.""" + log_write("socket_server", "info", f"Executing ev_close for alias: {alias}") + # Qui andrebbe la logica per chiudere la valvola + pass + +def close_all(): + """Placeholder per la funzione di chiusura di tutte le valvole.""" + log_write("socket_server", "info", "Executing close_all") + # Qui andrebbe la logica per chiudere tutte le valvole + pass + +def cron_disable_all_open_close(): + """Placeholder per la funzione di disabilitazione scheduling cron.""" + log_write("socket_server", "info", "Executing cron_disable_all_open_close") + # Qui andrebbe la logica per disabilitare lo scheduling + pass + +def cron_enable_all_open_close(): + """Placeholder per la funzione di abilitazione scheduling cron.""" + log_write("socket_server", "info", "Executing cron_enable_all_open_close") + # Qui andrebbe la logica per abilitare lo scheduling + pass + +def set_cron_init(): + """Placeholder per la funzione set_cron_init.""" + log_write("socket_server", "info", "Executing set_cron_init") + return "" # Simula output vuoto per successo + +def set_cron_start_socket_server(): + """Placeholder per la funzione set_cron_start_socket_server.""" + log_write("socket_server", "info", "Executing set_cron_start_socket_server") + return "" + +def set_cron_check_rain_sensor(): + """Placeholder per la funzione set_cron_check_rain_sensor.""" + log_write("socket_server", "info", "Executing set_cron_check_rain_sensor") + return "" + +def set_cron_check_rain_online(): + """Placeholder per la funzione set_cron_check_rain_online.""" + log_write("socket_server", "info", "Executing set_cron_check_rain_online") + return "" + +def set_cron_close_all_for_rain(): + """Placeholder per la funzione set_cron_close_all_for_rain.""" + log_write("socket_server", "info", "Executing set_cron_close_all_for_rain") + return "" + +def del_cron_open(arg): + """Placeholder per la funzione del_cron_open.""" + log_write("socket_server", "info", f"Executing del_cron_open with arg: {arg}") + return "" + +def del_cron_open_in(arg): + """Placeholder per la funzione del_cron_open_in.""" + log_write("socket_server", "info", f"Executing del_cron_open_in with arg: {arg}") + return "" + +def del_cron_close(arg): + """Placeholder per la funzione del_cron_close.""" + log_write("socket_server", "info", f"Executing del_cron_close with arg: {arg}") + return "" + +def add_cron_open(arg2, arg3, arg4, arg5, arg6, arg7, arg8): + """Placeholder per la funzione add_cron_open.""" + log_write("socket_server", "info", f"Executing add_cron_open with args: {arg2}, {arg3}, {arg4}, {arg5}, {arg6}, {arg7}, {arg8}") + return "" + +def add_cron_close(arg2, arg3, arg4, arg5, arg6, arg7, arg8): + """Placeholder per la funzione add_cron_close.""" + log_write("socket_server", "info", f"Executing add_cron_close with args: {arg2}, {arg3}, {arg4}, {arg5}, {arg6}, {arg7}, {arg8}") + return "" + +def cmd_pigardensched(arg2, arg3, arg4, arg5, arg6): + """Placeholder per la funzione cmd_pigardensched.""" + log_write("socket_server", "info", f"Executing cmd_pigardensched with args: {arg2}, {arg3}, {arg4}, {arg5}, {arg6}") + return "" + +def reset_last_rain_sensor_timestamp(): + """Placeholder per la funzione reset_last_rain_sensor_timestamp.""" + log_write("socket_server", "info", "Executing reset_last_rain_sensor_timestamp") + pass + +def reset_last_rain_online_timestamp(): + """Placeholder per la funzione reset_last_rain_online_timestamp.""" + log_write("socket_server", "info", "Executing reset_last_rain_online_timestamp") + pass + +def sensor_status_set(arg2, arg3, arg4): + """Placeholder per la funzione sensor_status_set.""" + log_write("socket_server", "info", f"Executing sensor_status_set with args: {arg2}, {arg3}, {arg4}") + pass + +# --- FUNZIONI DI GESTIONE DEI PROCESSI --- +def list_descendants(pid): + """ + Simula la logica di 'list_descendants' Bash. + In un sistema reale, dovresti usare librerie come 'psutil' + per ottenere i processi figli. Qui è solo una simulazione. + """ + try: + # psutil è l'opzione migliore, se disponibile + # import psutil + # parent = psutil.Process(pid) + # return [child.pid for child in parent.children(recursive=True)] + + # Fallback semplice per simulazione (non accurato per processi reali) + # Se non hai psutil, questa funzione è difficile da replicare accuratamente in modo generico. + # Potrebbe essere necessario un approccio specifico per il tuo sistema operativo. + return [] # Nessun discendente noto senza psutil + except Exception as e: + log_write("process_manager", "error", f"Errore nel listing dei discendenti di {pid}: {e}") + return [] + +def get_script_path(): + """Restituisce il percorso assoluto dello script corrente.""" + return os.path.abspath(sys.argv[0]) + +# --- CLASSE GESTORE RICHIESTE SOCKET --- +class MyTCPHandler(socketserver.BaseRequestHandler): + """ + La classe MyTCPHandler gestirà ogni nuova connessione client. + Sovrascrive il metodo handle() per implementare la logica del server. + """ + def handle(self): + global RUN_FROM_TCPSERVER + RUN_FROM_TCPSERVER = True + + # TCPREMOTEIP in Python è self.client_address[0] + client_ip = self.client_address[0] + log_write("socket_server", "info", f"Nuova connessione da: {client_ip}") + + try: + # Autenticazione (se configurata) + if TCPSERVER_USER and TCPSERVER_PWD: + # Leggi utente (timeout 3 secondi) + self.request.settimeout(3) + try: + user_line = self.request.recv(1024).decode('utf-8').strip() + password_line = self.request.recv(1024).decode('utf-8').strip() + except socket.timeout: + log_write("socket_server", "warning", f"socket connection from: {client_ip} - Timeout during credentials read") + json_error(0, "Authentication timeout") + return + + if user_line != TCPSERVER_USER or password_line != TCPSERVER_PWD: + log_write("socket_server", "warning", f"socket connection from: {client_ip} - Bad socket server credentials - user:{user_line}") + self.request.sendall(f'{{"status": "error", "code": 0, "message": "Bad socket server credentials"}}\n'.encode('utf-8')) + return + else: + log_write("socket_server", "info", f"socket connection from: {client_ip} - Authentication successful") + + # Leggi il comando + command_line = self.request.recv(4096).decode('utf-8').strip() + + # Parsing del comando + args = command_line.split(' ') + arg1 = args[0] if len(args) > 0 else "" + arg2 = args[1] if len(args) > 1 else "" + arg3 = args[2] if len(args) > 2 else "" + arg4 = args[3] if len(args) > 3 else "" + arg5 = args[4] if len(args) > 4 else "" + arg6 = args[5] if len(args) > 5 else "" + arg7 = args[6] if len(args) > 6 else "" + arg8 = args[7] if len(args) > 7 else "" + + log_write("socket_server", "info", f"socket connection from: {client_ip} - command: {command_line}") + + # Esegui il comando + response = self.execute_command(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) + + # Invia la risposta al client (assumendo che le funzioni json_error/json_status stampino su stdout) + # Potresti voler catturare l'output di quelle funzioni e inviarlo qui. + # Per semplicità, qui si presuppone che inviino direttamente al client, + # ma in un sistema reale dovresti costruire la risposta JSON e inviarla. + self.request.sendall(response.encode('utf-8') + b'\n') + + except socket.timeout: + log_write("socket_server", "warning", f"socket connection from: {client_ip} - Timeout waiting for command.") + self.request.sendall(f'{{"status": "error", "code": 0, "message": "Timeout waiting for command"}}\n'.encode('utf-8')) + except Exception as e: + log_write("socket_server", "error", f"Errore durante la gestione della connessione da {client_ip}: {e}") + self.request.sendall(f'{{"status": "error", "code": -1, "message": "Internal server error"}}\n'.encode('utf-8')) + finally: + self.request.close() # Chiudi la connessione + RUN_FROM_TCPSERVER = False # Reset per la prossima connessione o per operazioni interne + + def execute_command(self, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8): + """Esegue il comando ricevuto dal socket server.""" + # Questo metodo replicherà la logica della case statement Bash + vret = "" # Per catturare i risultati delle funzioni + + if arg1 == "status": + json_status(arg2, arg3, arg4, arg5, arg6, arg7) + elif arg1 == "open": + if not arg2: # "empty$arg2" == "empty" + json_error(0, "Alias solenoid not specified") + else: + ev_open(arg2, arg3) # &> /dev/null è gestito non catturando l'output + json_status(f"get_cron_open_in:{arg2}") + elif arg1 == "open_in": + ev_open_in(arg2, arg3, arg4, arg5) + json_status(f"get_cron_open_in:{arg4}") + elif arg1 == "close": + if not arg2: + json_error(0, "Alias solenoid not specified") + else: + ev_close(arg2) + json_status(f"get_cron_open_in:{arg2}") + elif arg1 == "close_all": + if arg2 == "disable_scheduling": + cron_disable_all_open_close() + close_all() + message_write("success", "All solenoid closed") + json_status() + elif arg1 == "cron_enable_all_open_close": + cron_enable_all_open_close() + message_write("success", "All solenoid enabled") + json_status() + elif arg1 == "set_general_cron": + # Per i comandi di set_general_cron, chiami le funzioni e concateni i risultati + # Ho ipotizzato che i risultati vuoti indichino successo e stringhe non vuote errori + funcs_to_call = { + "set_cron_init": set_cron_init, + "set_cron_start_socket_server": set_cron_start_socket_server, + "set_cron_check_rain_sensor": set_cron_check_rain_sensor, + "set_cron_check_rain_online": set_cron_check_rain_online, + "set_cron_close_all_for_rain": set_cron_close_all_for_rain, + } + + # Qui si itera sugli argomenti come nello script Bash + for i_arg in [arg for arg in [arg2, arg3, arg4, arg5, arg6, arg7] if arg]: + if i_arg in funcs_to_call: + ret_val = funcs_to_call[i_arg]() + if ret_val: # Se la funzione restituisce qualcosa (un errore in Bash) + vret += ret_val + + if vret: # Se c'è stato qualche errore in una delle chiamate + json_error(0, "Cron set failed") + log_write("socket_server", "error", f"Cron set failed: {vret}") + else: + message_write("success", "Cron set successful") + json_status() + + elif arg1 == "del_cron_open": + vret = del_cron_open(arg2) + if vret: + json_error(0, "Cron set failed") + log_write("socket_server", "error", f"Cron del failed: {vret}") + else: + message_write("success", "Cron set successful") + json_status() + + elif arg1 == "del_cron_open_in": + vret = del_cron_open_in(arg2) + if vret: + json_error(0, "Cron del failed") + log_write("socket_server", "error", f"Cron del failed: {vret}") + else: + message_write("success", "Scheduled start successfully deleted") + json_status(f"get_cron_open_in:{arg2}") + + elif arg1 == "del_cron_close": + vret = del_cron_close(arg2) + if vret: + json_error(0, "Cron set failed") + log_write("socket_server", "error", f"Cron set failed: {vret}") + else: + message_write("success", "Cron set successful") + json_status() + + elif arg1 == "add_cron_open": + vret = add_cron_open(arg2, arg3, arg4, arg5, arg6, arg7, arg8) + if vret: + json_error(0, "Cron set failed") + log_write("socket_server", "error", f"Cron set failed: {vret}") + else: + message_write("success", "Cron set successful") + json_status() + + elif arg1 == "add_cron_close": + vret = add_cron_close(arg2, arg3, arg4, arg5, arg6, arg7, arg8) + if vret: + json_error(0, "Cron set failed") + log_write("socket_server", "error", f"Cron set failed: {vret}") + else: + message_write("success", "Cron set successful") + json_status() + + elif arg1 == "cmd_pigardensched": + vret = cmd_pigardensched(arg2, arg3, arg4, arg5, arg6) + if vret: + json_error(0, "piGardenSched command failed") + log_write("socket_server", "error", f"piGardenSched command failed: {vret}") + else: + message_write("success", "Schedule set successful") + json_status() + + elif arg1 == "reboot": + message_write("warning", "System reboot is started") + json_status() + current_script_path = get_script_path() + # Esegui il reboot in un sottoprocesso separato per non bloccare il server + # e con nohup-like behavior. + # Questo è un esempio, la gestione di reboot/poweroff in Python è delicata. + # Potresti voler chiamare un comando di sistema come `sudo reboot`. + subprocess.Popen([current_script_path, "reboot_system_internal_cmd"], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, + preexec_fn=os.setsid) # setsid per disassociare dal gruppo di processi + + elif arg1 == "poweroff": + message_write("warning", "System shutdown is started") + json_status() + current_script_path = get_script_path() + subprocess.Popen([current_script_path, "poweroff_system_internal_cmd"], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, + preexec_fn=os.setsid) # setsid per disassociare dal gruppo di processi + + elif arg1 == "reset_last_rain_sensor_timestamp": + reset_last_rain_sensor_timestamp() + message_write("success", "Timestamp of last sensor rain successful reset") + json_status() + + elif arg1 == "reset_last_rain_online_timestamp": + reset_last_rain_online_timestamp() + message_write("success", "Timestamp of last online rain successful reset") + json_status() + + elif arg1 == "sensor_status_set": + if not arg2: + json_error(0, "Alias sensor not specified") + else: + sensor_status_set(arg2, arg3, arg4) + json_status() + + else: + json_error(0, "invalid command") + + # Le funzioni json_error, json_status, message_write stampano direttamente su stdout + # In un contesto di server reale, dovresti catturare il loro output e inviarlo al client. + # Per questa conversione, sto assumendo che tu voglia replicare il comportamento Bash + # di "stampa e poi la pipeline gestisce l'invio". Potrebbe richiedere un refactoring + # delle funzioni json_error/status per restituire stringhe invece di stampare. + # Per ora, restituisco una stringa vuota o un placeholder. + return "" # Potresti voler restituire il JSON o il messaggio qui + +# --- FUNZIONI PRINCIPALI DEL SERVER --- + +def start_socket_server(): + """Avvia il server socket.""" + # Rimuovi il file PID esistente + if os.path.exists(TCPSERVER_PID_FILE): + os.remove(TCPSERVER_PID_FILE) + + # Scrivi il PID dello script principale nel file PID + # In un sistema reale, dovresti scrivere il PID del processo del server, + # che potrebbe essere diverso se usi un manager di processi come systemd. + current_pid = os.getpid() + with open(TCPSERVER_PID_FILE, "w") as f: + f.write(str(current_pid)) + + log_write("socket_server", "info", f"Server PID {current_pid} scritto in {TCPSERVER_PID_FILE}") + + try: + # Crea il server TCP + # ThreadingTCPServer gestisce ogni richiesta in un thread separato + with socketserver.ThreadingTCPServer((TCPSERVER_IP, TCPSERVER_PORT), MyTCPHandler) as server: + log_write("socket_server", "info", f"Server socket avviato su {TCPSERVER_IP}:{TCPSERVER_PORT}") + # Avvia il server, rimarrà in ascolto indefinitamente + server.serve_forever() + except Exception as e: + log_write("socket_server", "error", f"Errore all'avvio del server socket: {e}") + if os.path.exists(TCPSERVER_PID_FILE): + os.remove(TCPSERVER_PID_FILE) # Pulisci il PID file in caso di errore + sys.exit(1) + + +def stop_socket_server(): + """Ferma il server socket.""" + if not os.path.exists(TCPSERVER_PID_FILE): + print("Daemon is not running") + sys.exit(1) + + log_write("socket_server", "info", "stop socket server") + + with open(TCPSERVER_PID_FILE, "r") as f: + try: + pid = int(f.read().strip()) + except ValueError: + print(f"Errore: Il file PID '{TCPSERVER_PID_FILE}' contiene un PID non valido.") + sys.exit(1) + + # Tentativo di killare i processi discendenti (se list_descendants è implementato) + descendants = list_descendants(pid) + for d_pid in descendants: + try: + os.kill(d_pid, 9) # SIGKILL + log_write("socket_server", "info", f"Terminato processo discendente {d_pid}") + except ProcessLookupError: + pass # Il processo non esiste + + # Tenta di killare il processo principale del server + try: + os.kill(pid, 9) # SIGKILL + log_write("socket_server", "info", f"Terminato processo server {pid}") + except ProcessLookupError: + print(f"Processo con PID {pid} non trovato.") + except Exception as e: + log_write("socket_server", "error", f"Errore durante l'uccisione del processo server {pid}: {e}") + + # Rimuovi il file PID + if os.path.exists(TCPSERVER_PID_FILE): + os.remove(TCPSERVER_PID_FILE) + log_write("socket_server", "info", "File PID rimosso.") + + +# --- FUNZIONE PRINCIPALE PER LA GESTIONE DEGLI ARGOMENTI DELLA CLI --- +if __name__ == "__main__": + if len(sys.argv) > 1: + command = sys.argv[1] + + if command == "start_socket_server": + # Per avviare in background come un vero daemon, dovresti implementare + # la demonizzazione (forking) qui o usare una libreria come `python-daemon`. + # Per ora, questo lo avvia nel terminale corrente. + log_write("main", "info", "Richiesta di avvio del socket server.") + start_socket_server() + elif command == "stop_socket_server": + log_write("main", "info", "Richiesta di stop del socket server.") + stop_socket_server() + elif command == "socket_server_command": + # Questa parte dovrebbe essere gestita dal server TCP stesso. + # Non viene chiamata direttamente dalla riga di comando in Python in questo modo. + # Se hai bisogno di testare la logica 'socket_server_command' isolatamente, + # dovresti chiamare MyTCPHandler.execute_command() con argomenti di test. + print("Errore: 'socket_server_command' non può essere chiamato direttamente come script.") + print("Questa logica è gestita internamente dal server TCP.") + sys.exit(1) + elif command == "reboot_system_internal_cmd": + # Comando interno per il riavvio effettivo + log_write("main", "warning", "Eseguo il riavvio del sistema...") + # In un sistema Linux, questo è il modo per riavviare + subprocess.run(["sudo", "reboot"]) + # Assicurati che l'utente che esegue lo script abbia i permessi sudo senza password per 'reboot' + elif command == "poweroff_system_internal_cmd": + # Comando interno per lo spegnimento effettivo + log_write("main", "warning", "Eseguo lo spegnimento del sistema...") + # In un sistema Linux, questo è il modo per spegnere + subprocess.run(["sudo", "poweroff"]) + # Assicurati che l'utente che esegue lo script abbia i permessi sudo senza password per 'poweroff' + else: + print(f"Comando non riconosciuto: {command}") + print("Utilizzo: python your_script_name.py [start_socket_server|stop_socket_server]") + sys.exit(1) + else: + print("Nessun comando specificato.") + print("Utilizzo: python your_script_name.py [start_socket_server|stop_socket_server]") + sys.exit(1) \ No newline at end of file