crossbario / crossbar

Crossbar.io - WAMP application router
https://crossbar.io/
Other
2.05k stars 275 forks source link

"crossbar stop" subcommand crashes on Windows #1802

Closed ghost closed 4 years ago

ghost commented 4 years ago

Crossbar version: Crossbar v20.5.1.dev1 Platform: Windows 10 Enterprise 1803

Error log:

C:\Users\xxxxxxxx>crossbar stop
Stopping Crossbar.io currently running from node directory C:\Users\xxxxxxxx (PID 11076) ...
Traceback (most recent call last):
  File "C:\Users\xxxxxxxx\AppData\Local\Programs\Python\Python38-32\Scripts\crossbar-script.py", line 33, in <module>
    sys.exit(load_entry_point('crossbar', 'console_scripts', 'crossbar')())
  File "c:\users\xxxxxxxx\ciot\projects\coaty\bindings\wamp\crossbar-router-from-source\crossbar\crossbar\__init__.py", line 203, in run
    return main(exename, args, reactor, personality_klass)
  File "c:\users\xxxxxxxx\ciot\projects\coaty\bindings\wamp\crossbar-router-from-source\crossbar\crossbar\node\main.py", line 1340, in main
    options.func(options, reactor=reactor, personality=personality)
  File "c:\users\xxxxxxxx\ciot\projects\coaty\bindings\wamp\crossbar-router-from-source\crossbar\crossbar\node\main.py", line 628, in _run_command_stop
    p.send_signal(signal.SIGINT)
  File "c:\users\xxxxxxxx\appdata\local\programs\python\python38-32\lib\site-packages\psutil\__init__.py", line 292, in wrapper
    return fun(self, *args, **kwargs)
  File "c:\users\xxxxxxxx\appdata\local\programs\python\python38-32\lib\site-packages\psutil\__init__.py", line 1214, in send_signal
    self._proc.send_signal(sig)
  File "c:\users\xxxxxxxx\appdata\local\programs\python\python38-32\lib\site-packages\psutil\_pswindows.py", line 679, in wrapper
    return fun(self, *args, **kwargs)
  File "c:\users\xxxxxxxx\appdata\local\programs\python\python38-32\lib\site-packages\psutil\_pswindows.py", line 874, in send_signal
    raise ValueError(
ValueError: only SIGTERM, CTRL_C_EVENT and CTRL_BREAK_EVENT signals are supported on Windows

Steps to reproduce:

Issue:

In Python on Windows, you cannot send a SIGINT signal to terminate a process, you have to use SIGTERM or process.terminate instead. Invoking SIGINT raises a ValueError which is not handled by your program.

Fix:

In node/main.py add special cases for Windows, like this:

def _run_command_stop(options, reactor, personality):
    """
    Subcommand "crossbar stop".
    """
    # check if there is a Crossbar.io instance currently running from
    # the Crossbar.io node directory at all
    #
    pid_data = _check_is_running(options.cbdir)
    if pid_data:
        pid = pid_data['pid']
        print("Stopping Crossbar.io currently running from node directory {} (PID {}) ...".format(options.cbdir, pid))
        if not _HAS_PSUTIL:
            if sys.platform == 'win32':
                # Windows doesn't accept SIGINT
                os.kill(pid, signal.SIGTERM)
                print("SIGTERM sent to process {}.".format(pid))
            else:
                os.kill(pid, signal.SIGINT)
                print("SIGINT sent to process {}.".format(pid))
        else:
            p = psutil.Process(pid)
            try:
                # first try to interrupt (orderly shutdown)
                _INTERRUPT_TIMEOUT = 5
                # On Windows, SIGINT raises ValueError which is caught below.
                p.send_signal(signal.SIGINT)
                print("SIGINT sent to process {} .. waiting for exit ({} seconds) ...".format(pid, _INTERRUPT_TIMEOUT))
                p.wait(timeout=_INTERRUPT_TIMEOUT)
            except (ValueError, psutil.TimeoutExpired):
                print("... process {} still alive - will _terminate_ now.".format(pid))
                try:
                    _TERMINATE_TIMEOUT = 5
                    p.terminate()
                    print("SIGTERM sent to process {} .. waiting for exit ({} seconds) ...".format(pid, _TERMINATE_TIMEOUT))
                    p.wait(timeout=_TERMINATE_TIMEOUT)
                except psutil.TimeoutExpired:
                    print("... process {} still alive - will KILL now.".format(pid))
                    p.kill()
                    print("SIGKILL sent to process {}.".format(pid))
                else:
                    print("Process {} terminated.".format(pid))
            else:
                print("Process {} has excited gracefully.".format(pid))
        sys.exit(0)
    else:
        print("No Crossbar.io is currently running from node directory {}.".format(options.cbdir))
        sys.exit(getattr(os, 'EX_UNAVAILABLE', 1))
oberstet commented 4 years ago

thanks for all the details! just 1 Q: does this approach require crossbar to be run with local admim rights on windows (to be able to send a SIGTERM - even though the worker processes had been spawned from the controller process which also send the signal)?

ghost commented 4 years ago

@oberstet Oops, good question! On my company PC my account has local admin rights and SIGTERM works. Both "crossbar start" and "stop" commands are running without elevated command prompt. This is the output of the "crossbar stop" command with the fix applied:

C:\Users\xxxxxxx>crossbar stop
Stopping Crossbar.io currently running from node directory C:\Users\xxxxxx (PID 3328) ...
... process 3328 still alive - will _terminate_ now.
SIGTERM sent to process 3328 .. waiting for exit (5 seconds) ...
Process 3328 terminated.

Not sure if this still works for accounts without local admin rights... Unfortunately, I can't test it easily, as company group policies prevent me from creating such a local account on my company PC.

ghost commented 4 years ago

@oberstet I just found a way to create a new local account without admin rights on my Win 10 machine. Hope to have some time to install and test crossbar this afternoon. I'll keep you informed...

ghost commented 4 years ago

@oberstet Good news! My patched version of "crossbar stop" also works on a local Windows account without Administrator privileges (account is just part of USER group). So yes, SIGTERM will do the job!

oberstet commented 4 years ago

fixed via https://github.com/crossbario/crossbar/pull/1803

oberstet commented 4 years ago

published as part of https://github.com/crossbario/crossbar/tree/v20.8.1

ghost commented 4 years ago

Thanks a lot!