Foro de elhacker.net

Programación => Scripting => Mensaje iniciado por: Drakaris en 9 Enero 2022, 18:45 pm



Título: Ctrl+C en windows me lo detecta pero no sale del script | Python3
Publicado por: Drakaris en 9 Enero 2022, 18:45 pm
Buenas,
Feliz año  ;D

Estoy con un socket servidor que al estar corriendo quiero que si el usuario hace Ctrl+C en el CMD este sale limpiamente (con un SIGINT).

main.py
Código
  1. from sources.handlerSignals import HandlerSignals
  2. class SocketServer:
  3.    def __init__(self,**kwargs):
  4.        try:
  5.            HandlerSignals()
  6.            # [...]
  7.  
  8.        # When the service shutdown successfully
  9.        except (KeyboardInterrupt, SystemExit,GeneratorExit) as err:
  10.            print("KeyboardInterrupt")
  11.            None
  12.  
  13.        finally:
  14.            try:
  15.                self.sock.close()
  16.            except:
  17.                None
  18.            print("Turnning off ClassAdminS")
  19.            sys.exit(0)
  20.  
  21.    # continue code
  22. if __name__=="__main__":
  23.    SocketServer(args=sys.argv)
  24.  

El HandlerSignals() esta en una carpeta 'sources' en el archivo handlerSignals
  -main.py
  |
  - sources/
      \-- handlerSignals.py


handlerSignals.py
Código
  1. import signal, sys, platform
  2. class HandlerSignals:
  3.    def __init__(self):
  4.        signal.signal(signal.SIGTERM,self.shutdown)
  5.  
  6.        # shutdown signal in Windows
  7.        if platform.system().upper()=="WINDOWS":
  8.            import win32api
  9.            win32api.SetConsoleCtrlHandler(self.shutdownWin, True)
  10.  
  11.    def shutdownWin(self,a):
  12.        print("Ctrl+c")
  13.        raise SystemExit
  14.  
  15.    def shutdown(self,code,msg):
  16.        raise SystemExit
  17.  


Como se puede ver si el sistema es Windows, se ejecuta
Citar
win32api.SetConsoleCtrlHandler(self.shutdownWin, True)
Que esta en escucha de Ctrl+C en windows CMD. Así que al ser presionado se llama a la funcion shutdownWin que este imprime "Ctrl+C" y lanza una excepcion SystemExit.

Que esta excepcion teoricamente es recogida por main.py que y sale de la terminal.

Pero no ocurre eso. Al lanzarse el SystemExit no imprime "KeyboardInterrupt". Y no se porque pasa....


https://imgur.com/gallery/x5jxQhM

Parece como si la excepcion no se pueda pasar al main.py

Como lo puedo solucionar...? Gracias por vuestra atencion.


Título: Re: Ctrl+C en windows me lo detecta pero no sale del script | Python3
Publicado por: Drakaris en 10 Enero 2022, 10:25 am
Buenos dias!
Solución
Ya esta resuelto. Puede que no sea la mejor opcion, ni la más adecuada, pero me veo obligado ha hacerlo así pues Windows tiende a matar los procesos.

Así como lo queria hacer tendría que funcionar para mis ojos, pero la realidad es que no, y no se porque (en Linux funcionaria....).

Lo que hice es añadir el HandlerSignals() justo despues de crear el socket (dentro del __createSocket, despues del listen)

main.py:
Código
  1. #!/usr/bin/python3
  2. import sys,socket,urllib3,time,multiprocessing, threading
  3. from sources.ClientListener import ClientListener
  4. from sources.handlerSignals import HandlerSignals
  5. from sources.utils import logFile, Hosts, getIpAddress, Notify
  6. from sources.Server import Server
  7. from sources.Requests import Requests
  8.  
  9. class SocketServer:
  10.    def __init__(self,**kwargs):
  11.        try:
  12.            # [...]
  13.            self.__createSocket()
  14.  
  15.        # When the service shutdown successfully
  16.        except (KeyboardInterrupt, SystemExit,GeneratorExit) as err:
  17.                None
  18.  
  19.        finally:
  20.            try:
  21.                self.sock.close()
  22.            except:
  23.                None
  24.            Notify("Turnning off ClassAdminS",logFile().message("Good Bye. What you have a good day :)", True, "INFO"))
  25.  
  26.    def __createSocket(self):
  27.        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  28.        self.sock.bind(("",self.PORT))
  29.        self.sock.listen(self.CLIENTS)
  30.        HandlerSignals(self.sock)
  31.        Notify("Start ClassAdminS",logFile().message(f"Good days :D. I am listenning {self.CLIENTS} clients by port {self.PORT}.",True,"INFO"))
  32.        self.__handlerClients()
  33.  
  34. if __name__=="__main__":
  35.    SocketServer(args=sys.argv)
  36.  

La diferencia es que la funcion HnadlerSignals(sock) coge como parametro la variable que contiene el objecto socket.

handlerSignals.py
Código
  1. import signal, platform
  2. class HandlerSignals:
  3.    def __init__(self,sock):
  4.        self.sock = sock
  5.        signal.signal(signal.SIGTERM,self.shutdown)
  6.        # shutdown signal in Windows
  7.        if platform.system().upper()=="WINDOWS":
  8.            import win32api
  9.            win32api.SetConsoleCtrlHandler(self.shutdownWin, True)
  10.  
  11.    def shutdownWin(self,a):
  12.        self.sock.close()
  13.  
  14.    def shutdown(self,code,msg):
  15.        raise SystemExit
  16.  
La gran diferencia de handlerSignals.py con el anterior es que, al hacer Ctrl+C y llamar a la funcion shutdownWin(), este cierra el socket y me muestra el Notify de el try/except/finally del main.py.

Así me funciona correctamente, aunque no se si es muy buena practica,pero me funciona.

Hay que tener en cuenta que el HandlerSignals() debe estar justo después de la declaración de la variable self.sock. Yo lo pongo despues del self.sock.listen().

https://imgur.com/gallery/B2VR7Lp

¿Porque tan pesado con el Ctrl+C en CMD?
Por dos cosas:
  • Pues para salir limpiamente y correctamente
  • Pues este script lo ejecuto como servicio con NSSM

NSSM al parar un servicio, implicitamente hace un Ctrl+C y espera una reaccion. Si no hay reaccion este se cierra forzosamente y muestra el siguiente mensaje.
https://imgur.com/gallery/n5UO4sL

Ahora si el script detecta el Ctrl+C en CMD....
https://imgur.com/gallery/qLnBGOi

En el NSSM este es configurado así
https://imgur.com/gallery/ZEwvmOq

Gracias por vuestra atencion! ;-)