From baeb605e53c2e8a7dceee86035f6d4cc6b49e131 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Mon, 19 May 2014 17:51:58 -0300 Subject: Improve wait and quit process. Refactor logic from backend to the vpnprocess. --- src/leap/bitmask/app.py | 2 + src/leap/bitmask/backend.py | 29 ++++++++---- src/leap/bitmask/gui/mainwindow.py | 69 ++++++++++++++++++----------- src/leap/bitmask/services/eip/vpnprocess.py | 12 +++++ 4 files changed, 77 insertions(+), 35 deletions(-) diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index e413ab4c..05f81f0b 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -76,6 +76,7 @@ def sigint_handler(*args, **kwargs): mainwindow = args[0] mainwindow.quit() + def sigterm_handler(*args, **kwargs): """ Signal handler for SIGTERM. @@ -87,6 +88,7 @@ def sigterm_handler(*args, **kwargs): mainwindow = args[0] mainwindow.quit() + def add_logger_handlers(debug=False, logfile=None, replace_stdout=True): """ Create the logger and attach the handlers. diff --git a/src/leap/bitmask/backend.py b/src/leap/bitmask/backend.py index 9661bda0..b138fd8d 100644 --- a/src/leap/bitmask/backend.py +++ b/src/leap/bitmask/backend.py @@ -17,7 +17,6 @@ """ Backend for everything """ -import commands import logging import os import time @@ -391,14 +390,20 @@ class EIP(object): # TODO: are we connected here? signaler.signal(signaler.EIP_CONNECTED) - def stop(self, shutdown=False): + def _do_stop(self, shutdown=False): """ - Stop the service. + Stop the service. This is run in a thread to avoid blocking. """ self._vpn.terminate(shutdown) if IS_LINUX: self._wait_for_firewall_down() + def stop(self, shutdown=False): + """ + Stop the service. + """ + return threads.deferToThread(self._do_stop, shutdown) + def _wait_for_firewall_down(self): """ Wait for the firewall to come down. @@ -411,15 +416,16 @@ class EIP(object): MAX_FW_WAIT_RETRIES = 25 FW_WAIT_STEP = 0.5 - retry = 0 - - fw_up_cmd = "pkexec /usr/sbin/bitmask-root firewall isup" - fw_is_down = lambda: commands.getstatusoutput(fw_up_cmd)[0] == 256 + retry = 1 - while retry < MAX_FW_WAIT_RETRIES: - if fw_is_down(): + while retry <= MAX_FW_WAIT_RETRIES: + if self._vpn.is_fw_down(): + self._signaler.signal(self._signaler.EIP_STOPPED) return else: + msg = "Firewall is not down yet, waiting... {0} of {1}" + msg = msg.format(retry, MAX_FW_WAIT_RETRIES) + logger.debug(msg) time.sleep(FW_WAIT_STEP) retry += 1 logger.warning("After waiting, firewall is not down... " @@ -953,6 +959,7 @@ class Signaler(QtCore.QObject): eip_disconnected = QtCore.Signal(object) eip_connection_died = QtCore.Signal(object) eip_connection_aborted = QtCore.Signal(object) + eip_stopped = QtCore.Signal(object) # EIP problems eip_no_polkit_agent_error = QtCore.Signal(object) @@ -1040,6 +1047,8 @@ class Signaler(QtCore.QObject): EIP_DISCONNECTED = "eip_disconnected" EIP_CONNECTION_DIED = "eip_connection_died" EIP_CONNECTION_ABORTED = "eip_connection_aborted" + EIP_STOPPED = "eip_stopped" + EIP_NO_POLKIT_AGENT_ERROR = "eip_no_polkit_agent_error" EIP_NO_TUN_KEXT_ERROR = "eip_no_tun_kext_error" EIP_NO_PKEXEC_ERROR = "eip_no_pkexec_error" @@ -1110,6 +1119,8 @@ class Signaler(QtCore.QObject): self.EIP_DISCONNECTED, self.EIP_CONNECTION_DIED, self.EIP_CONNECTION_ABORTED, + self.EIP_STOPPED, + self.EIP_NO_POLKIT_AGENT_ERROR, self.EIP_NO_TUN_KEXT_ERROR, self.EIP_NO_PKEXEC_ERROR, diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 557f3828..7cc88c1f 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -19,7 +19,6 @@ Main window for Bitmask. """ import logging import socket -import time from datetime import datetime @@ -87,6 +86,7 @@ class MainWindow(QtGui.QMainWindow): raise_window = QtCore.Signal([]) soledad_ready = QtCore.Signal([]) logout = QtCore.Signal([]) + all_services_stopped = QtCore.Signal() # We use this flag to detect abnormal terminations user_stopped_eip = False @@ -195,6 +195,12 @@ class MainWindow(QtGui.QMainWindow): self._logged_user = None self._logged_in_offline = False + # Set used to track the services being stopped and need wait. + self._services_being_stopped = {} + + # timeout object used to trigger quit + self._quit_timeout_callater = None + self._backend_connected_signals = {} self._backend_connect() @@ -1996,6 +2002,14 @@ class MainWindow(QtGui.QMainWindow): self._cancel_ongoing_defers() + self._services_being_stopped = {'imap', 'eip'} + + imap_stopped = lambda: self._remove_service('imap') + self._backend.signaler.imap_stopped.connect(imap_stopped) + + eip_stopped = lambda: self._remove_service('eip') + self._backend.signaler.eip_stopped.connect(eip_stopped) + logger.debug('Stopping mail services') self._backend.stop_imap_service() self._backend.stop_smtp_service() @@ -2007,27 +2021,6 @@ class MainWindow(QtGui.QMainWindow): logger.debug('Terminating vpn') self._backend.stop_eip(shutdown=True) - # We need to give some time to the ongoing signals for shutdown - # to come into action. This needs to be solved using - # back-communication from backend. - # TODO: handle this, I commented this fix to merge 'cleanly' - # QtCore.QTimer.singleShot(3000, self._shutdown) - - def _shutdown(self): - """ - Actually shutdown. - """ - self._cancel_ongoing_defers() - - # TODO missing any more cancels? - - logger.debug('Cleaning pidfiles') - self._cleanup_pidfiles() - if self._quit_callback: - self._quit_callback() - - logger.debug('Bye.') - def quit(self): """ Start the quit sequence and wait for services to finish. @@ -2060,10 +2053,27 @@ class MainWindow(QtGui.QMainWindow): self._really_quit = True - # call final_quit when imap is stopped - self._backend.signaler.imap_stopped.connect(self.final_quit) + # call final quit when all the services are stopped + self.all_services_stopped.connect(self.final_quit) # or if we reach the timeout - reactor.callLater(self.SERVICES_STOP_TIMEOUT, self._backend.stop) + self._quit_timeout_callater = reactor.callLater( + self.SERVICES_STOP_TIMEOUT, self.final_quit) + + @QtCore.Slot() + def _remove_service(self, service): + """ + Remove the given service from the waiting list and check if we have + running services that we need to wait until we quit. + Emit self.all_services_stopped signal if we don't need to keep waiting. + + :param service: the service that we want to remove + :type service: str + """ + self._services_being_stopped.discard(service) + + if not self._services_being_stopped: + logger.debug("All services stopped.") + self.all_services_stopped.emit() @QtCore.Slot() def final_quit(self): @@ -2075,10 +2085,15 @@ class MainWindow(QtGui.QMainWindow): try: # disconnect signal if we get here due a timeout. - self._backend.signaler.imap_stopped.disconnect(self.final_quit) + self.all_services_stopped.disconnect(self.final_quit) except RuntimeError: pass # Signal was not connected + # Cancel timeout to avoid being called if we reached here through the + # signal + if self._quit_timeout_callater.active(): + self._quit_timeout_callater.cancel() + # Remove lockfiles on a clean shutdown. logger.debug('Cleaning pidfiles') if IS_WIN: @@ -2086,3 +2101,5 @@ class MainWindow(QtGui.QMainWindow): self._backend.stop() self.close() + + reactor.callLater(1, self._quit_callback) diff --git a/src/leap/bitmask/services/eip/vpnprocess.py b/src/leap/bitmask/services/eip/vpnprocess.py index 734b88df..81eac6d9 100644 --- a/src/leap/bitmask/services/eip/vpnprocess.py +++ b/src/leap/bitmask/services/eip/vpnprocess.py @@ -17,6 +17,7 @@ """ VPN Manager, spawned in a custom processProtocol. """ +import commands import logging import os import shutil @@ -232,6 +233,17 @@ class VPN(object): BM_ROOT, "firewall", "start"] + gateways) return True if exitCode is 0 else False + def is_fw_down(self): + """ + Return whether the firewall is down or not. + + :rtype: bool + """ + BM_ROOT = linuxvpnlauncher.LinuxVPNLauncher.BITMASK_ROOT + fw_up_cmd = "pkexec {0} firewall isup".format(BM_ROOT) + fw_is_down = lambda: commands.getstatusoutput(fw_up_cmd)[0] == 256 + return fw_is_down() + def _tear_down_firewall(self): """ Tear the firewall down using the privileged wrapper. -- cgit v1.2.3