From 01a7faa2033ef3ce85bc5a346eca3601f0f4f7c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Fri, 8 Mar 2013 11:09:23 -0300 Subject: Migrate VPN process to QProcess Also: - Add a new tray icon for the whole app and a VPN specific one - Add a way to start/stop EIP independently - Improve reaction to the process dying --- src/leap/gui/mainwindow.py | 98 ++++++++++++++++++++++++++++++++++++++----- src/leap/gui/ui/mainwindow.ui | 63 +++++++++++++++------------- src/leap/services/eip/vpn.py | 29 ++++++++----- 3 files changed, 142 insertions(+), 48 deletions(-) (limited to 'src') diff --git a/src/leap/gui/mainwindow.py b/src/leap/gui/mainwindow.py index 50a03fb9..df21a2bb 100644 --- a/src/leap/gui/mainwindow.py +++ b/src/leap/gui/mainwindow.py @@ -56,6 +56,9 @@ class MainWindow(QtGui.QMainWindow): self.CONNECTED_ICON = QtGui.QPixmap(":/images/conn_connected.png") self.ERROR_ICON = QtGui.QPixmap(":/images/conn_error.png") + self.LOGGED_OUT_ICON = QtGui.QPixmap(":/images/leap-gray-big.png") + self.LOGGED_IN_ICON = QtGui.QPixmap(":/images/leap-color-big.png") + self.ui = Ui_MainWindow() self.ui.setupUi(self) @@ -67,6 +70,10 @@ class MainWindow(QtGui.QMainWindow): self.ui.stackedWidget.setCurrentIndex(self.LOGIN_INDEX) + self.ui.btnEipStartStop.setEnabled(False) + self.ui.btnEipStartStop.clicked.connect( + self._stop_eip) + # This is loaded only once, there's a bug when doing that more # than once self._provider_config = ProviderConfig() @@ -105,11 +112,13 @@ class MainWindow(QtGui.QMainWindow): self._eip_bootstrapper.download_config.connect( self._intermediate_stage) self._eip_bootstrapper.download_client_certificate.connect( - self._start_eip) + self._finish_eip_bootstrap) self._vpn = VPN() self._vpn.state_changed.connect(self._update_vpn_state) self._vpn.status_changed.connect(self._update_vpn_status) + self._vpn.process_finished.connect( + self._eip_finished) QtCore.QCoreApplication.instance().connect( QtCore.QCoreApplication.instance(), @@ -130,6 +139,22 @@ class MainWindow(QtGui.QMainWindow): self._really_quit = False self._systray = None + self._vpn_systray = None + + self._action_eip_status = QtGui.QAction("Encryption is OFF", self) + self._action_eip_status.setEnabled(False) + self._action_eip_stop = QtGui.QAction("Stop", self) + self._action_eip_stop.triggered.connect( + self._stop_eip) + self._action_eip_write = QtGui.QAction( + QtGui.QIcon(":/images/Arrow-Up-32.png"), + "0.0 Kb", self) + self._action_eip_write.setEnabled(False) + self._action_eip_read = QtGui.QAction( + QtGui.QIcon(":/images/Arrow-Down-32.png"), + "0.0 Kb", self) + self._action_eip_read.setEnabled(False) + self._action_visible = QtGui.QAction("Hide", self) self._action_visible.triggered.connect(self._toggle_visible) @@ -179,10 +204,20 @@ class MainWindow(QtGui.QMainWindow): systrayMenu.addAction(self.ui.action_quit) self._systray = QtGui.QSystemTrayIcon(self) self._systray.setContextMenu(systrayMenu) - self._systray.setIcon(QtGui.QIcon(self.ERROR_ICON)) + self._systray.setIcon(QtGui.QIcon(self.LOGGED_OUT_ICON)) self._systray.setVisible(True) self._systray.activated.connect(self._toggle_visible) + vpn_systrayMenu = QtGui.QMenu(self) + vpn_systrayMenu.addAction(self._action_eip_status) + vpn_systrayMenu.addAction(self._action_eip_stop) + vpn_systrayMenu.addAction(self._action_eip_read) + vpn_systrayMenu.addAction(self._action_eip_write) + self._vpn_systray = QtGui.QSystemTrayIcon(self) + self._vpn_systray.setContextMenu(vpn_systrayMenu) + self._vpn_systray.setIcon(QtGui.QIcon(self.ERROR_ICON)) + self._vpn_systray.setVisible(False) + def _toggle_visible(self): """ SLOT @@ -455,8 +490,33 @@ class MainWindow(QtGui.QMainWindow): triggers the eip bootstrapping """ self.ui.stackedWidget.setCurrentIndex(self.EIP_STATUS_INDEX) + self._systray.setIcon(self.LOGGED_IN_ICON) self._download_eip_config() + def _start_eip(self): + self._vpn.start(eipconfig=self._eip_config, + providerconfig=self._provider_config, + socket_host="/home/chiiph/vpnsock", + socket_port="unix") + self._vpn_systray.setVisible(True) + self.ui.btnEipStartStop.setEnabled(True) + self.ui.btnEipStartStop.setText("Stop EIP") + self.ui.btnEipStartStop.clicked.disconnect( + self._start_eip) + self.ui.btnEipStartStop.clicked.connect( + self._stop_eip) + + def _stop_eip(self): + self._vpn.set_should_quit() + self._vpn_systray.setVisible(False) + self._set_eip_status("EIP has stopped") + self._set_eip_status_icon("error") + self.ui.btnEipStartStop.setText("Start EIP") + self.ui.btnEipStartStop.clicked.disconnect( + self._stop_eip) + self.ui.btnEipStartStop.clicked.connect( + self._start_eip) + def _download_eip_config(self): """ Starts the EIP bootstrapping sequence @@ -483,13 +543,16 @@ class MainWindow(QtGui.QMainWindow): @type status: str """ selected_pixmap = self.ERROR_ICON + tray_message = "Encryption is OFF" if status in ("AUTH", "GET_CONFIG"): selected_pixmap = self.CONNECTING_ICON elif status in ("CONNECTED"): + tray_message = "Encryption is ON" selected_pixmap = self.CONNECTED_ICON self.ui.lblVPNStatusIcon.setPixmap(selected_pixmap) - self._systray.setIcon(QtGui.QIcon(selected_pixmap)) + self._vpn_systray.setIcon(QtGui.QIcon(selected_pixmap)) + self._action_eip_status.setText(tray_message) def _update_vpn_state(self, data): """ @@ -520,12 +583,16 @@ class MainWindow(QtGui.QMainWindow): """ upload = float(data[self._vpn.TUNTAP_WRITE_KEY]) upload = upload / 1000.0 - self.ui.lblUpload.setText("%s Kb" % (upload,)) + upload_str = "%s Kb" % (upload,) + self.ui.lblUpload.setText(upload_str) + self._action_eip_write.setText(upload_str) download = float(data[self._vpn.TUNTAP_READ_KEY]) download = download / 1000.0 - self.ui.lblDownload.setText("%s Kb" % (download,)) + download_str = "%s Kb" % (download,) + self.ui.lblDownload.setText(download_str) + self._action_eip_read.setText(download_str) - def _start_eip(self, data): + def _finish_eip_bootstrap(self, data): """ SLOT TRIGGER: self._eip_bootstrapper.download_client_certificate @@ -542,10 +609,7 @@ class MainWindow(QtGui.QMainWindow): self._provider_config .get_domain(), "eip-service.json")): - self._vpn.start(eipconfig=self._eip_config, - providerconfig=self._provider_config, - socket_host="/home/chiiph/vpnsock", - socket_port="unix") + self._start_eip() # TODO: display a message if the EIP configuration cannot be # loaded @@ -569,12 +633,14 @@ class MainWindow(QtGui.QMainWindow): logging out """ self._set_status(message) + self._vpn_systray.setIcon(self.LOGGED_OUT_ICON) self.ui.action_sign_out.setEnabled(False) self.ui.stackedWidget.setCurrentIndex(self.LOGIN_INDEX) self.ui.lnPassword.setText("") self._login_set_enabled(True) self._set_status("") self._vpn.set_should_quit() + self._vpn_systray.setVisible(False) def _intermediate_stage(self, data): """ @@ -594,6 +660,18 @@ class MainWindow(QtGui.QMainWindow): self._login_set_enabled(True) self._set_status(data[self._provider_bootstrapper.ERROR_KEY]) + def _eip_finished(self, exitCode): + """ + SLOT + TRIGGERS: + self._vpn.process_finished + + Triggered when the EIP/VPN process finishes to set the UI + accordingly + """ + logger.debug("Finished VPN with exitCode %s" % (exitCode,)) + self._stop_eip() + if __name__ == "__main__": import signal from functools import partial diff --git a/src/leap/gui/ui/mainwindow.ui b/src/leap/gui/ui/mainwindow.ui index a527eaf6..d8a6d37d 100644 --- a/src/leap/gui/ui/mainwindow.ui +++ b/src/leap/gui/ui/mainwindow.ui @@ -171,16 +171,6 @@ - - - - Disconnected - - - Qt::AlignCenter - - - @@ -194,20 +184,7 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + @@ -220,7 +197,7 @@ - + @@ -233,7 +210,7 @@ - + Qt::Horizontal @@ -246,7 +223,7 @@ - + @@ -256,7 +233,7 @@ - + @@ -266,6 +243,36 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Disconnected + + + Qt::AlignCenter + + + + + + + Start EIP + + + diff --git a/src/leap/services/eip/vpn.py b/src/leap/services/eip/vpn.py index 88692442..71944f50 100644 --- a/src/leap/services/eip/vpn.py +++ b/src/leap/services/eip/vpn.py @@ -22,7 +22,6 @@ import logging import sys from PySide import QtCore, QtGui -from subprocess import Popen, PIPE from functools import partial from leap.config.providerconfig import ProviderConfig @@ -46,6 +45,8 @@ class VPN(QtCore.QThread): state_changed = QtCore.Signal(dict) status_changed = QtCore.Signal(dict) + process_finished = QtCore.Signal(int) + CONNECTION_RETRY_TIME = 1000 POLL_TIME = 100 @@ -69,7 +70,6 @@ class VPN(QtCore.QThread): self._launcher = get_platform_launcher() self._subp = None - self._started = False self._tn = None self._host = None @@ -100,15 +100,14 @@ class VPN(QtCore.QThread): return try: - self._disconnect() + self._send_command("signal SIGTERM") + self._tn.close() self._subp.terminate() except Exception as e: logger.debug("Could not terminate process, trying command " + "signal SIGNINT: %r" % (e,)) - self._send_command("signal SIGINT") - self._subp.wait() - self.wait() - self._started = False + finally: + self._tn = None def start(self, eipconfig, providerconfig, socket_host, socket_port): """ @@ -128,7 +127,7 @@ class VPN(QtCore.QThread): leap_assert_type(eipconfig, EIPConfig) leap_assert(providerconfig, "We need a provider config") leap_assert_type(providerconfig, ProviderConfig) - leap_assert(not self._started, "Starting process more than once!") + leap_assert(not self.isRunning(), "Starting process more than once!") logger.debug("Starting VPN...") @@ -140,8 +139,12 @@ class VPN(QtCore.QThread): socket_host=socket_host, socket_port=socket_port) try: - self._subp = Popen(command, stdout=PIPE, stderr=PIPE, - bufsize=1, close_fds=ON_POSIX) + self._subp = QtCore.QProcess() + self._subp.finished.connect(self.process_finished) + self._subp.start(command[:1][0], command[1:]) + logger.debug("Waiting for started...") + self._subp.waitForStarted() + logger.debug("Started!") self._host = socket_host self._port = socket_port @@ -296,12 +299,18 @@ class VPN(QtCore.QThread): logger.debug("Quitting VPN thread") return + if self._subp and self._subp.state() != QtCore.QProcess.Running: + QtCore.QThread.msleep(self.CONNECTION_RETRY_TIME) + if self._tn is None: self._connect(self._host, self._port) QtCore.QThread.msleep(self.CONNECTION_RETRY_TIME) else: self._parse_state_and_notify(self._send_command("state")) self._parse_status_and_notify(self._send_command("status")) + output_sofar = self._subp.readAllStandardOutput() + if len(output_sofar) > 0: + logger.debug(output_sofar) QtCore.QThread.msleep(self.POLL_TIME) -- cgit v1.2.3