summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Alejandro <ivanalejandro0@gmail.com>2014-07-02 16:43:58 -0300
committerIvan Alejandro <ivanalejandro0@gmail.com>2014-07-14 12:15:25 -0300
commitc3f485e194eb32939755178b11d472e1e69a94ad (patch)
treeaf773573bbc494bbc85c257e634441cb67d23f3c
parenteab69607ba4a65acf5c7745134d74917c76c6bf8 (diff)
Handle SIGINT/SIGTERM in processes.
-rw-r--r--src/leap/bitmask/app.py36
-rw-r--r--src/leap/bitmask/backend_app.py26
-rw-r--r--src/leap/bitmask/frontend_app.py43
3 files changed, 86 insertions, 19 deletions
diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py
index d1a2a111..fa244470 100644
--- a/src/leap/bitmask/app.py
+++ b/src/leap/bitmask/app.py
@@ -44,6 +44,8 @@ import os
import signal
import sys
+from functools import partial
+
from leap.bitmask.backend.utils import generate_certificates
from leap.bitmask import __version__ as VERSION
@@ -109,12 +111,33 @@ def do_mail_plumbing(opts):
# XXX catch when import is used w/o acct
+def sigterm_handler(logger, gui_process, backend_process, signum, frame):
+ """
+ Signal handler that quits the running app cleanly.
+
+ :param logger: the configured logger object.
+ :type logger: logging.Logger
+ :param gui_process: the GUI process
+ :type gui_process: multiprocessing.Process
+ :param backend_process: the backend process
+ :type backend_process: multiprocessing.Process
+ :param signum: number of the signal received (e.g. SIGINT -> 2)
+ :type signum: int
+ :param frame: current stack frame
+ :type frame: frame or None
+ """
+ logger.debug("SIGTERM catched, terminating processes.")
+ gui_process.terminate()
+ # Don't terminate the backend, the frontend takes care of that.
+ # backend_process.terminate()
+
+
def start_app():
"""
Starts the main event loop and launches the main window.
"""
- # Ensure that the application quits using CTRL-C
- signal.signal(signal.SIGINT, signal.SIG_DFL)
+ # Ignore the signals since we handle them in the subprocesses
+ # signal.signal(signal.SIGINT, signal.SIG_IGN)
# Parse arguments and store them
opts = leap_argparse.get_options()
@@ -181,13 +204,18 @@ def start_app():
flags_dict = flags_to_dict()
app = lambda: run_frontend(options, flags_dict)
- gui_process = multiprocessing.Process(target=app)
+ gui_process = multiprocessing.Process(target=app, name='Frontend')
gui_process.start()
backend = lambda: run_backend(opts.danger, flags_dict)
- backend_process = multiprocessing.Process(target=backend)
+ backend_process = multiprocessing.Process(target=backend, name='Backend')
backend_process.start()
+ handle_sigterm = partial(sigterm_handler, logger,
+ gui_process, backend_process)
+ signal.signal(signal.SIGTERM, handle_sigterm)
+ signal.signal(signal.SIGINT, handle_sigterm)
+
if __name__ == "__main__":
start_app()
diff --git a/src/leap/bitmask/backend_app.py b/src/leap/bitmask/backend_app.py
index d4815d82..b6d00f2d 100644
--- a/src/leap/bitmask/backend_app.py
+++ b/src/leap/bitmask/backend_app.py
@@ -14,11 +14,32 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import logging
+import multiprocessing
import signal
from leap.bitmask.backend.leapbackend import LeapBackend
from leap.bitmask.util import dict_to_flags
+logger = logging.getLogger(__name__)
+
+
+def signal_handler(signum, frame):
+ """
+ Signal handler that quits the running app cleanly.
+
+ :param signum: number of the signal received (e.g. SIGINT -> 2)
+ :type signum: int
+ :param frame: current stack frame
+ :type frame: frame or None
+ """
+ # Note: we don't stop the backend in here since the frontend signal handler
+ # will take care of that.
+ # In the future we may need to do the stop in here when the frontend and
+ # the backend are run separately (without multiprocessing)
+ pname = multiprocessing.current_process().name
+ logger.debug("{0}: SIGNAL #{1} catched.".format(pname, signum))
+
def run_backend(bypass_checks, flags_dict):
"""
@@ -29,8 +50,9 @@ def run_backend(bypass_checks, flags_dict):
:param flags_dict: a dict containing the flag values set on app start.
:type flags_dict: dict
"""
- # Ensure that the application quits using CTRL-C
- signal.signal(signal.SIGINT, signal.SIG_DFL)
+ # ignore SIGINT since app.py takes care of signaling SIGTERM to us.
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
+ signal.signal(signal.SIGTERM, signal_handler)
dict_to_flags(flags_dict)
diff --git a/src/leap/bitmask/frontend_app.py b/src/leap/bitmask/frontend_app.py
index 1fe4cd0a..5dc42287 100644
--- a/src/leap/bitmask/frontend_app.py
+++ b/src/leap/bitmask/frontend_app.py
@@ -14,10 +14,13 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import multiprocessing
import signal
import sys
import os
+from functools import partial
+
from PySide import QtCore, QtGui
from leap.bitmask.config import flags
@@ -29,15 +32,20 @@ import logging
logger = logging.getLogger(__name__)
-def sigint_handler(*args, **kwargs):
+def signal_handler(window, signum, frame):
"""
- Signal handler for SIGINT
+ Signal handler that quits the running app cleanly.
+
+ :param window: a window with a `quit` callable
+ :type window: MainWindow
+ :param signum: number of the signal received (e.g. SIGINT -> 2)
+ :type signum: int
+ :param frame: current stack frame
+ :type frame: frame or None
"""
- logger = kwargs.get('logger', None)
- if logger:
- logger.debug("SIGINT catched. shutting down...")
- mainwindow = args[0]
- mainwindow.quit()
+ pname = multiprocessing.current_process().name
+ logger.debug("{0}: SIGNAL #{1} catched.".format(pname, signum))
+ window.quit()
def run_frontend(options, flags_dict):
@@ -79,12 +87,21 @@ def run_frontend(options, flags_dict):
qApp.setApplicationName("leap")
qApp.setOrganizationDomain("leap.se")
- MainWindow(start_hidden=start_hidden)
-
- # sigint_window = partial(sigint_handler, window, logger=logger)
- # signal.signal(signal.SIGINT, sigint_window)
- # Ensure that the application quits using CTRL-C
- signal.signal(signal.SIGINT, signal.SIG_DFL)
+ # HACK:
+ # We need to do some 'python work' once in a while, otherwise, no python
+ # code will be called and the Qt event loop will prevent the signal
+ # handlers for SIGINT/SIGTERM to be called.
+ # see: http://stackoverflow.com/a/4939113/687989
+ timer = QtCore.QTimer()
+ timer.start(500) # You may change this if you wish.
+ timer.timeout.connect(lambda: None) # Let the interpreter run each 500 ms.
+
+ window = MainWindow(start_hidden=start_hidden)
+
+ sigterm_handler = partial(signal_handler, window)
+ # ignore SIGINT since app.py takes care of signaling SIGTERM to us.
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
+ signal.signal(signal.SIGTERM, sigterm_handler)
sys.exit(qApp.exec_())