diff --git a/kolibri/utils/cli.py b/kolibri/utils/cli.py index 3d5fffcde3b..a515a1b35aa 100644 --- a/kolibri/utils/cli.py +++ b/kolibri/utils/cli.py @@ -24,6 +24,7 @@ from . import server # noqa from .system import become_daemon # noqa +from .sanity_checks import check_other_kolibri_running # noqa USAGE = """ Kolibri @@ -262,12 +263,9 @@ def start(port=None, daemon=True): # https://github.com/learningequality/kolibri/issues/1615 update() - if port is None: - try: - port = int(os.environ['KOLIBRI_LISTEN_PORT']) - except ValueError: - logger.error("Invalid KOLIBRI_LISTEN_PORT, must be an integer") - raise + # In case that some tests run start() function only + if not isinstance(port, int): + port = _get_port(port) if not daemon: logger.info("Running 'kolibri start' in foreground...") @@ -314,6 +312,7 @@ def stop(): pid, __, __ = server.get_status() server.stop(pid=pid) stopped = True + logger.info("Kolibri server has been successfully stoppped.") except server.NotRunning as e: verbose_status = "{msg:s} ({code:d})".format( code=e.status_code, @@ -576,6 +575,17 @@ def parse_args(args=None): return docopt(USAGE, **docopt_kwargs), django_args +def _get_port(port): + port = int(port) if port else None + if port is None: + try: + port = int(os.environ['KOLIBRI_LISTEN_PORT']) + except ValueError: + logger.error("Invalid KOLIBRI_LISTEN_PORT, must be an integer") + raise + return port + + def main(args=None): """ Kolibri's main function. Parses arguments and calls utility functions. @@ -589,6 +599,10 @@ def main(args=None): debug = arguments['--debug'] + if arguments['start']: + port = _get_port(arguments['--port']) + check_other_kolibri_running(port) + initialize(debug=debug) # Alias @@ -607,8 +621,6 @@ def main(args=None): return if arguments['start']: - port = arguments['--port'] - port = int(port) if port else None daemon = not arguments['--foreground'] if sys.platform == 'darwin': daemon = False diff --git a/kolibri/utils/sanity_checks.py b/kolibri/utils/sanity_checks.py new file mode 100644 index 00000000000..429112e8aea --- /dev/null +++ b/kolibri/utils/sanity_checks.py @@ -0,0 +1,42 @@ +import logging +import socket +import sys + +from .server import get_status +from .server import NotRunning + +logger = logging.getLogger(__name__) + +def check_other_kolibri_running(port): + try: + # Check if there are other kolibri instances running + # If there are, then we need to stop users from starting kolibri again. + pid, listen_address, listen_port = get_status() + logger.error( + "There is another Kolibri server running. " + "Please use `kolibri stop` and try again.") + sys.exit(1) + + except NotRunning: + # In case that something other than Kolibri occupies the port, + # check the port's availability. + check_port_availability('127.0.0.1', port) + + +def check_port_availability(host, port): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + # This is to prevent the previous execution has left the socket + # in a TIME_WAIT start, and can't be immediately reused. + # From the bottom of https://docs.python.org/2/library/socket.html#example + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + try: + s.bind((host, port)) + s.close() + except socket.error: + # Port is occupied + logger.error( + "Port {} is occupied.\n" + "Please check that you do not have other processes " + "running on this port and try again.\n".format(port)) + s.close() + sys.exit(1)