From 451189d369f6661a67a1692945e68b5128cb9a65 Mon Sep 17 00:00:00 2001 From: antialias Date: Thu, 16 Aug 2012 16:06:53 -0700 Subject: Cleaned up files and file names using the PEP 8 style guide. --- src/leap/eip/openvpnconnection.py | 408 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 408 insertions(+) create mode 100644 src/leap/eip/openvpnconnection.py (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py new file mode 100644 index 00000000..a26059a7 --- /dev/null +++ b/src/leap/eip/openvpnconnection.py @@ -0,0 +1,408 @@ +""" +OpenVPN Connection +""" + +from __future__ import (print_function) +import logging +import os +import socket +import telnetlib +import time +from functools import partial + +logger = logging.getLogger(name=__name__) + +from leap.utils.coroutines import spawn_and_watch_process +from leap.baseapp.config import get_config +from leap.Connection import Connection + +class OpenVPNConnection(Connection): + """ + All related to invocation + of the openvpn binary + """ + # Connection Methods + + def __init__(self, config_file=None, watcher_cb=None,host="/tmp/.eip.sock", port="unix", password=None): + #XXX FIXME + #change watcher_cb to line_observer + """ + :param config_file: configuration file to read from + :param watcher_cb: callback to be \ +called for each line in watched stdout + :param signal_map: dictionary of signal names and callables \ +to be triggered for each one of them. + :type config_file: str + :type watcher_cb: function + :type signal_map: dict + """ + + self.config_file = config_file + self.watcher_cb = watcher_cb + #self.signal_maps = signal_maps + + self.subp = None + self.watcher = None + + self.server = None + self.port = None + self.proto = None + + self.autostart = True + + self._get_config() + + #Get this info from the Configuration Class + #XXX hardcoded host here. change. + self.host = host + if isinstance(port, str) and port.isdigit(): + port = int(port) + self.port = port + self.password = password + self.tn = None + + #XXX workaround for signaling + #the ui that we don't know how to + #manage a connection error + self.with_errors = False + + + def _set_command_mockup(self): + """ + sets command and args for a command mockup + that just mimics the output from the real thing + """ + command, args = get_vpn_stdout_mockup() + self.command, self.args = command, args + + def _get_config(self): + """ + retrieves the config options from defaults or + home file, or config file passed in command line. + """ + config = get_config(config_file=self.config_file) + self.config = config + + if config.has_option('openvpn', 'command'): + commandline = config.get('openvpn', 'command') + if commandline == "mockup": + self._set_command_mockup() + return + command_split = commandline.split(' ') + command = command_split[0] + if len(command_split) > 1: + args = command_split[1:] + else: + args = [] + self.command = command + #print("debug: command = %s" % command) + self.args = args + else: + self._set_command_mockup() + + if config.has_option('openvpn', 'autostart'): + autostart = config.get('openvpn', 'autostart') + self.autostart = autostart + + def _launch_openvpn(self): + """ + invocation of openvpn binaries in a subprocess. + """ + #XXX TODO: + #deprecate watcher_cb, + #use _only_ signal_maps instead + + if self.watcher_cb is not None: + linewrite_callback = self.watcher_cb + else: + #XXX get logger instead + linewrite_callback = lambda line: print('watcher: %s' % line) + + observers = (linewrite_callback, + partial(self.status_watcher, self.status)) + subp, watcher = spawn_and_watch_process( + self.command, + self.args, + observers=observers) + self.subp = subp + self.watcher = watcher + + conn_result = self.status.CONNECTED + return conn_result + + def _try_connection(self): + """ + attempts to connect + """ + if self.subp is not None: + print('cowardly refusing to launch subprocess again') + return + self._launch_openvpn() + + def cleanup(self): + """ + terminates child subprocess + """ + if self.subp: + self.subp.terminate() + + + #Here are the actual code to manage OpenVPN Connection + #TODO: Look into abstraction them and moving them up into base class + # this code based on code from cube-routed project + + """ + Run commands over OpenVPN management interface + and parses the output. + """ + # XXX might need a lock to avoid + # race conditions here... + + def forget_errors(self): + print('forgetting errors') + self.with_errors = False + + def connect(self): + """Connect to openvpn management interface""" + try: + self.close() + except: + #XXX don't like this general + #catch here. + pass + if self.connected(): + return True + self.tn = UDSTelnet(self.host, self.port) + + # XXX make password optional + # specially for win plat. we should generate + # the pass on the fly when invoking manager + # from conductor + + #self.tn.read_until('ENTER PASSWORD:', 2) + #self.tn.write(self.password + '\n') + #self.tn.read_until('SUCCESS:', 2) + + self._seek_to_eof() + self.forget_errors() + return True + + def _seek_to_eof(self): + """ + Read as much as available. Position seek pointer to end of stream + """ + b = self.tn.read_eager() + while b: + b = self.tn.read_eager() + + def connected(self): + """ + Returns True if connected + rtype: bool + """ + #return bool(getattr(self, 'tn', None)) + try: + assert self.tn + return True + except: + #XXX get rid of + #this pokemon exception!!! + return False + + def close(self, announce=True): + """ + Close connection to openvpn management interface + """ + if announce: + self.tn.write("quit\n") + self.tn.read_all() + self.tn.get_socket().close() + del self.tn + + def _send_command(self, cmd, tries=0): + """ + Send a command to openvpn and return response as list + """ + if tries > 3: + return [] + if not self.connected(): + try: + self.connect() + except MissingSocketError: + #XXX capture more helpful error + #messages + #pass + return self.make_error() + try: + self.tn.write(cmd + "\n") + except socket.error: + logger.error('socket error') + print('socket error!') + self.close(announce=False) + self._send_command(cmd, tries=tries + 1) + return [] + buf = self.tn.read_until(b"END", 2) + self._seek_to_eof() + blist = buf.split('\r\n') + if blist[-1].startswith('END'): + del blist[-1] + return blist + else: + return [] + + def _send_short_command(self, cmd): + """ + parse output from commands that are + delimited by "success" instead + """ + if not self.connected(): + self.connect() + self.tn.write(cmd + "\n") + # XXX not working? + buf = self.tn.read_until(b"SUCCESS", 2) + self._seek_to_eof() + blist = buf.split('\r\n') + return blist + + # + # useful vpn commands + # + + def pid(self): + #XXX broken + return self._send_short_command("pid") + + def make_error(self): + """ + capture error and wrap it in an + understandable format + """ + #XXX get helpful error codes + self.with_errors = True + now = int(time.time()) + return '%s,LAUNCHER ERROR,ERROR,-,-' % now + + def state(self): + """ + OpenVPN command: state + """ + state = self._send_command("state") + if not state: + return None + if isinstance(state, str): + return state + if isinstance(state, list): + if len(state) == 1: + return state[0] + else: + return state[-1] + + def status(self): + """ + OpenVPN command: status + """ + status = self._send_command("status") + return status + + def status2(self): + """ + OpenVPN command: last 2 statuses + """ + return self._send_command("status 2") + + # + # parse info + # + + def get_status_io(self): + status = self.status() + if isinstance(status, str): + lines = status.split('\n') + if isinstance(status, list): + lines = status + try: + (header, when, tun_read, tun_write, + tcp_read, tcp_write, auth_read) = tuple(lines) + except ValueError: + return None + + when_ts = time.strptime(when.split(',')[1], "%a %b %d %H:%M:%S %Y") + sep = ',' + # XXX cleanup! + tun_read = tun_read.split(sep)[1] + tun_write = tun_write.split(sep)[1] + tcp_read = tcp_read.split(sep)[1] + tcp_write = tcp_write.split(sep)[1] + auth_read = auth_read.split(sep)[1] + + # XXX this could be a named tuple. prettier. + return when_ts, (tun_read, tun_write, tcp_read, tcp_write, auth_read) + + def get_connection_state(self): + state = self.state() + if state is not None: + ts, status_step, ok, ip, remote = state.split(',') + ts = time.gmtime(float(ts)) + # XXX this could be a named tuple. prettier. + return ts, status_step, ok, ip, remote + + def status_watcher(self, cs, line): + """ + a wrapper that calls to ConnectionStatus object + :param cs: a EIPConnectionStatus instance + :type cs: EIPConnectionStatus object + :param line: a single line of the watched output + :type line: str + """ + #print('status watcher watching') + + # from the mullvad code, should watch for + # things like: + # "Initialization Sequence Completed" + # "With Errors" + # "Tap-Win32" + + if "Completed" in line: + cs.change_to(cs.CONNECTED) + return + + if "Initial packet from" in line: + cs.change_to(cs.CONNECTING) + return + + + +class MissingSocketError(Exception): + pass + + +class ConnectionRefusedError(Exception): + pass + +class UDSTelnet(telnetlib.Telnet): + + def open(self, host, port=23, timeout=socket._GLOBAL_DEFAULT_TIMEOUT): + """Connect to a host. If port is 'unix', it + will open a connection over unix docmain sockets. + + The optional second argument is the port number, which + defaults to the standard telnet port (23). + + Don't try to reopen an already connected instance. + """ + self.eof = 0 + self.host = host + self.port = port + self.timeout = timeout + + if self.port == "unix": + # unix sockets spoken + if not os.path.exists(self.host): + raise MissingSocketError + self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + try: + self.sock.connect(self.host) + except socket.error: + raise ConnectionRefusedError + else: + self.sock = socket.create_connection((host, port), timeout) -- cgit v1.2.3 From f5948577939dce4f85dd86f37c0823a0a852e074 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 21 Aug 2012 03:11:32 +0900 Subject: fix imports + style cleaning --- src/leap/eip/openvpnconnection.py | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index a26059a7..d3ce3578 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -12,9 +12,10 @@ from functools import partial logger = logging.getLogger(name=__name__) -from leap.utils.coroutines import spawn_and_watch_process -from leap.baseapp.config import get_config -from leap.Connection import Connection +from leap.util.coroutines import spawn_and_watch_process +from leap.eip.config import get_config +from leap.base.connection import Connection + class OpenVPNConnection(Connection): """ @@ -23,7 +24,13 @@ class OpenVPNConnection(Connection): """ # Connection Methods - def __init__(self, config_file=None, watcher_cb=None,host="/tmp/.eip.sock", port="unix", password=None): + def __init__(self, config_file=None, + watcher_cb=None, + debug=False, + host="/tmp/.eip.sock", + port="unix", + password=None, + *args, **kwargs): #XXX FIXME #change watcher_cb to line_observer """ @@ -66,20 +73,20 @@ to be triggered for each one of them. #manage a connection error self.with_errors = False - - def _set_command_mockup(self): - """ - sets command and args for a command mockup - that just mimics the output from the real thing - """ - command, args = get_vpn_stdout_mockup() - self.command, self.args = command, args + #def _set_command_mockup(self): + #""" + #sets command and args for a command mockup + #that just mimics the output from the real thing + #""" + #command, args = get_vpn_stdout_mockup() + #self.command, self.args = command, args def _get_config(self): """ retrieves the config options from defaults or home file, or config file passed in command line. """ + #XXX merge! was changed in test-eip branch!!! config = get_config(config_file=self.config_file) self.config = config @@ -97,8 +104,8 @@ to be triggered for each one of them. self.command = command #print("debug: command = %s" % command) self.args = args - else: - self._set_command_mockup() + #else: + #self._set_command_mockup() if config.has_option('openvpn', 'autostart'): autostart = config.get('openvpn', 'autostart') -- cgit v1.2.3 From 738b4bf8c6b75a1d73b7fa3e1a5edb69adf9d8a0 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 21 Aug 2012 04:58:05 +0900 Subject: fix out-of-sync refactor. manually merge changes from the develop branch that were lost due to having branched a previous state when refactored former "conductor" class. also, moved more exceptions to its own file. --- src/leap/eip/openvpnconnection.py | 231 +++++++++++++++++++------------------- 1 file changed, 114 insertions(+), 117 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index d3ce3578..601bb54a 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -1,20 +1,20 @@ """ OpenVPN Connection """ - from __future__ import (print_function) import logging -import os import socket -import telnetlib import time from functools import partial logger = logging.getLogger(name=__name__) -from leap.util.coroutines import spawn_and_watch_process -from leap.eip.config import get_config from leap.base.connection import Connection +from leap.util.coroutines import spawn_and_watch_process + +from leap.eip.udstelnet import UDSTelnet +from leap.eip import config as eip_config +from leap.eip import exceptions as eip_exceptions class OpenVPNConnection(Connection): @@ -43,6 +43,8 @@ to be triggered for each one of them. :type watcher_cb: function :type signal_map: dict """ + self.debug = debug + #print('conductor:%s' % debug) self.config_file = config_file self.watcher_cb = watcher_cb @@ -55,12 +57,29 @@ to be triggered for each one of them. self.port = None self.proto = None + self.missing_pkexec = False + self.missing_auth_agent = False + self.bad_keyfile_perms = False + self.missing_vpn_keyfile = False + self.missing_provider = False + self.bad_provider = False + + #XXX workaround for signaling + #the ui that we don't know how to + #manage a connection error + self.with_errors = False + + self.command = None + self.args = None + self.autostart = True + self._get_or_create_config() + self._check_vpn_keys() - self._get_config() + # + # management init methods + # - #Get this info from the Configuration Class - #XXX hardcoded host here. change. self.host = host if isinstance(port, str) and port.isdigit(): port = int(port) @@ -68,48 +87,92 @@ to be triggered for each one of them. self.password = password self.tn = None - #XXX workaround for signaling - #the ui that we don't know how to - #manage a connection error - self.with_errors = False - - #def _set_command_mockup(self): - #""" - #sets command and args for a command mockup - #that just mimics the output from the real thing - #""" - #command, args = get_vpn_stdout_mockup() - #self.command, self.args = command, args - - def _get_config(self): - """ - retrieves the config options from defaults or - home file, or config file passed in command line. - """ - #XXX merge! was changed in test-eip branch!!! - config = get_config(config_file=self.config_file) - self.config = config + def _set_autostart(self): + config = self.config + if config.has_option('openvpn', 'autostart'): + autostart = config.getboolean('openvpn', + 'autostart') + self.autostart = autostart + else: + if config.has_option('DEFAULT', 'autostart'): + autostart = config.getboolean('DEFAULT', + 'autostart') + self.autostart = autostart + def _set_ovpn_command(self): + config = self.config if config.has_option('openvpn', 'command'): commandline = config.get('openvpn', 'command') - if commandline == "mockup": - self._set_command_mockup() - return + command_split = commandline.split(' ') command = command_split[0] if len(command_split) > 1: args = command_split[1:] else: args = [] + + self.command = command + self.args = args + else: + # no command in config, we build it up. + # XXX check also for command-line --command flag + try: + command, args = eip_config.build_ovpn_command( + config, + debug=self.debug) + except eip_exceptions.EIPNoPolkitAuthAgentAvailable: + command = args = None + self.missing_auth_agent = True + except eip_exceptions.EIPNoPkexecAvailable: + command = args = None + self.missing_pkexec = True + + # XXX if not command, signal error. self.command = command - #print("debug: command = %s" % command) self.args = args - #else: - #self._set_command_mockup() - if config.has_option('openvpn', 'autostart'): - autostart = config.get('openvpn', 'autostart') - self.autostart = autostart + def _check_ovpn_config(self): + """ + checks if there is a default openvpn config. + if not, it writes one with info from the provider + definition file + """ + # TODO + # - get --with-openvpn-config from opts + try: + eip_config.check_or_create_default_vpnconf(self.config) + except eip_exceptions.EIPInitNoProviderError: + logger.error('missing default provider definition') + self.missing_provider = True + except eip_exceptions.EIPInitBadProviderError: + logger.error('bad provider definition') + self.bad_provider = True + + def _get_or_create_config(self): + """ + retrieves the config options from defaults or + home file, or config file passed in command line. + populates command and args to be passed to subprocess. + """ + config = eip_config.get_config( + config_file=self.config_file) + self.config = config + + self._set_autostart() + self._set_ovpn_command() + self._check_ovpn_config() + + def _check_vpn_keys(self): + """ + checks for correct permissions on vpn keys + """ + try: + eip_config.check_vpn_keys(self.config) + except eip_exceptions.EIPInitNoKeyFileError: + self.missing_vpn_keyfile = True + except eip_exceptions.EIPInitBadKeyFilePermError: + logger.error('error while checking vpn keys') + self.bad_keyfile_perms = True def _launch_openvpn(self): """ @@ -126,7 +189,7 @@ to be triggered for each one of them. linewrite_callback = lambda line: print('watcher: %s' % line) observers = (linewrite_callback, - partial(self.status_watcher, self.status)) + partial(lambda: None, self.status)) subp, watcher = spawn_and_watch_process( self.command, self.args, @@ -134,13 +197,12 @@ to be triggered for each one of them. self.subp = subp self.watcher = watcher - conn_result = self.status.CONNECTED - return conn_result - def _try_connection(self): """ attempts to connect """ + if self.command is None: + raise eip_exceptions.EIPNoCommandError if self.subp is not None: print('cowardly refusing to launch subprocess again') return @@ -153,17 +215,14 @@ to be triggered for each one of them. if self.subp: self.subp.terminate() - - #Here are the actual code to manage OpenVPN Connection - #TODO: Look into abstraction them and moving them up into base class - # this code based on code from cube-routed project - - """ - Run commands over OpenVPN management interface - and parses the output. - """ - # XXX might need a lock to avoid - # race conditions here... + # + # management methods + # + # XXX REVIEW-ME + # REFACTOR INFO: (former "manager". + # Can we move to another + # base class to test independently?) + # def forget_errors(self): print('forgetting errors') @@ -182,7 +241,7 @@ to be triggered for each one of them. self.tn = UDSTelnet(self.host, self.port) # XXX make password optional - # specially for win plat. we should generate + # specially for win. we should generate # the pass on the fly when invoking manager # from conductor @@ -207,7 +266,6 @@ to be triggered for each one of them. Returns True if connected rtype: bool """ - #return bool(getattr(self, 'tn', None)) try: assert self.tn return True @@ -235,7 +293,7 @@ to be triggered for each one of them. if not self.connected(): try: self.connect() - except MissingSocketError: + except eip_exceptions.MissingSocketError: #XXX capture more helpful error #messages #pass @@ -352,64 +410,3 @@ to be triggered for each one of them. ts = time.gmtime(float(ts)) # XXX this could be a named tuple. prettier. return ts, status_step, ok, ip, remote - - def status_watcher(self, cs, line): - """ - a wrapper that calls to ConnectionStatus object - :param cs: a EIPConnectionStatus instance - :type cs: EIPConnectionStatus object - :param line: a single line of the watched output - :type line: str - """ - #print('status watcher watching') - - # from the mullvad code, should watch for - # things like: - # "Initialization Sequence Completed" - # "With Errors" - # "Tap-Win32" - - if "Completed" in line: - cs.change_to(cs.CONNECTED) - return - - if "Initial packet from" in line: - cs.change_to(cs.CONNECTING) - return - - - -class MissingSocketError(Exception): - pass - - -class ConnectionRefusedError(Exception): - pass - -class UDSTelnet(telnetlib.Telnet): - - def open(self, host, port=23, timeout=socket._GLOBAL_DEFAULT_TIMEOUT): - """Connect to a host. If port is 'unix', it - will open a connection over unix docmain sockets. - - The optional second argument is the port number, which - defaults to the standard telnet port (23). - - Don't try to reopen an already connected instance. - """ - self.eof = 0 - self.host = host - self.port = port - self.timeout = timeout - - if self.port == "unix": - # unix sockets spoken - if not os.path.exists(self.host): - raise MissingSocketError - self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - try: - self.sock.connect(self.host) - except socket.error: - raise ConnectionRefusedError - else: - self.sock = socket.create_connection((host, port), timeout) -- cgit v1.2.3 From d908247dcc2cac66d31f081d892a04833206de3b Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 21 Aug 2012 05:43:55 +0900 Subject: fix status_watcher callback stub --- src/leap/eip/openvpnconnection.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 601bb54a..81e6b1ba 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -7,6 +7,7 @@ import socket import time from functools import partial +logging.basicConfig() logger = logging.getLogger(name=__name__) from leap.base.connection import Connection @@ -182,14 +183,20 @@ to be triggered for each one of them. #deprecate watcher_cb, #use _only_ signal_maps instead + logger.debug('_launch_openvpn called') if self.watcher_cb is not None: linewrite_callback = self.watcher_cb else: #XXX get logger instead linewrite_callback = lambda line: print('watcher: %s' % line) + # the partial is not + # being applied now because we're not observing the process + # stdout like we did in the early stages. but I leave it + # here since it will be handy for observing patterns in the + # thru-the-manager updates (with regex) observers = (linewrite_callback, - partial(lambda: None, self.status)) + partial(lambda con_status, line: None, self.status)) subp, watcher = spawn_and_watch_process( self.command, self.args, @@ -235,7 +242,7 @@ to be triggered for each one of them. except: #XXX don't like this general #catch here. - pass + raise if self.connected(): return True self.tn = UDSTelnet(self.host, self.port) @@ -250,7 +257,7 @@ to be triggered for each one of them. #self.tn.read_until('SUCCESS:', 2) self._seek_to_eof() - self.forget_errors() + #self.forget_errors() return True def _seek_to_eof(self): @@ -290,6 +297,8 @@ to be triggered for each one of them. """ if tries > 3: return [] + if self.tn is None: + return [] if not self.connected(): try: self.connect() -- cgit v1.2.3 From 560232609ef229d46932f8ffcd66b8e114e8b3e6 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 22 Aug 2012 01:30:37 +0900 Subject: yay! First WORKING GUI in refactor branch :) Obviously then, you should ignore the commit message in 489ed46140d6d. That commit WAS NOT working, believe me :) Fix an annoying bug by which we were overwriting the "connect" method that came from vpnmanager with basically an empty stub. --- src/leap/eip/openvpnconnection.py | 48 +++++++++++++++------------------------ 1 file changed, 18 insertions(+), 30 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 81e6b1ba..a230d229 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -9,6 +9,7 @@ from functools import partial logging.basicConfig() logger = logging.getLogger(name=__name__) +logger.setLevel(logging.DEBUG) from leap.base.connection import Connection from leap.util.coroutines import spawn_and_watch_process @@ -86,7 +87,7 @@ to be triggered for each one of them. port = int(port) self.port = port self.password = password - self.tn = None + #self.tn = None def _set_autostart(self): config = self.config @@ -235,16 +236,11 @@ to be triggered for each one of them. print('forgetting errors') self.with_errors = False - def connect(self): + def connect_to_management(self): """Connect to openvpn management interface""" - try: + #logger.debug('connecting socket') + if hasattr(self, 'tn'): self.close() - except: - #XXX don't like this general - #catch here. - raise - if self.connected(): - return True self.tn = UDSTelnet(self.host, self.port) # XXX make password optional @@ -273,47 +269,39 @@ to be triggered for each one of them. Returns True if connected rtype: bool """ - try: - assert self.tn - return True - except: - #XXX get rid of - #this pokemon exception!!! - return False + return hasattr(self, 'tn') def close(self, announce=True): """ Close connection to openvpn management interface """ + logger.debug('closing socket') if announce: self.tn.write("quit\n") self.tn.read_all() self.tn.get_socket().close() del self.tn - def _send_command(self, cmd, tries=0): + def _send_command(self, cmd): """ Send a command to openvpn and return response as list """ - if tries > 3: - return [] - if self.tn is None: - return [] + #logger.debug('connected? %s' % self.connected()) if not self.connected(): try: - self.connect() + #logger.debug('try to connect') + self.connect_to_management() except eip_exceptions.MissingSocketError: #XXX capture more helpful error - #messages - #pass return self.make_error() + except: + raise try: - self.tn.write(cmd + "\n") + if hasattr(self, 'tn'): + self.tn.write(cmd + "\n") except socket.error: logger.error('socket error') - print('socket error!') self.close(announce=False) - self._send_command(cmd, tries=tries + 1) return [] buf = self.tn.read_until(b"END", 2) self._seek_to_eof() @@ -371,14 +359,14 @@ to be triggered for each one of them. else: return state[-1] - def status(self): + def vpn_status(self): """ OpenVPN command: status """ status = self._send_command("status") return status - def status2(self): + def vpn_status2(self): """ OpenVPN command: last 2 statuses """ @@ -389,7 +377,7 @@ to be triggered for each one of them. # def get_status_io(self): - status = self.status() + status = self.vpn_status() if isinstance(status, str): lines = status.split('\n') if isinstance(status, list): -- cgit v1.2.3 From 5ab2163a89ad7bc303f436af738aa0e7e6bb24d4 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 22 Aug 2012 04:37:41 +0900 Subject: fix for failing bad port init test --- src/leap/eip/openvpnconnection.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index a230d229..3972b617 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -85,9 +85,12 @@ to be triggered for each one of them. self.host = host if isinstance(port, str) and port.isdigit(): port = int(port) + elif port == "unix": + port = "unix" + else: + port = None self.port = port self.password = password - #self.tn = None def _set_autostart(self): config = self.config -- cgit v1.2.3 From 06883461f2daa616b2e3c842f53d9422703cd9c7 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 28 Aug 2012 23:08:39 +0900 Subject: eip_checks called from main app. removed "configuration" object. checks are called from conductor. --- src/leap/eip/openvpnconnection.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 3972b617..5f67d27a 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -59,11 +59,14 @@ to be triggered for each one of them. self.port = None self.proto = None + # XXX move all error messages + # into a more encapsulated object. self.missing_pkexec = False self.missing_auth_agent = False self.bad_keyfile_perms = False self.missing_vpn_keyfile = False self.missing_provider = False + self.missing_definition = False self.bad_provider = False #XXX workaround for signaling -- cgit v1.2.3 From d4de193b52881590c07468bdfece5f82fa48840d Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 30 Aug 2012 06:05:49 +0900 Subject: remove unused function --- src/leap/eip/openvpnconnection.py | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 5f67d27a..1f2f6d8c 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -139,23 +139,6 @@ to be triggered for each one of them. self.command = command self.args = args - def _check_ovpn_config(self): - """ - checks if there is a default openvpn config. - if not, it writes one with info from the provider - definition file - """ - # TODO - # - get --with-openvpn-config from opts - try: - eip_config.check_or_create_default_vpnconf(self.config) - except eip_exceptions.EIPInitNoProviderError: - logger.error('missing default provider definition') - self.missing_provider = True - except eip_exceptions.EIPInitBadProviderError: - logger.error('bad provider definition') - self.bad_provider = True - def _get_or_create_config(self): """ retrieves the config options from defaults or @@ -168,7 +151,6 @@ to be triggered for each one of them. self._set_autostart() self._set_ovpn_command() - self._check_ovpn_config() def _check_vpn_keys(self): """ -- cgit v1.2.3 From 6c4012fc128c5af1b75cf33eef00590cf0e82438 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 31 Aug 2012 04:39:13 +0900 Subject: deprecated configparser. closes #500 --- src/leap/eip/openvpnconnection.py | 69 ++++++++++++--------------------------- 1 file changed, 20 insertions(+), 49 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 1f2f6d8c..32fa55b1 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -77,8 +77,10 @@ to be triggered for each one of them. self.command = None self.args = None + # XXX get autostart from config self.autostart = True - self._get_or_create_config() + #self._get_or_create_config() + self._set_ovpn_command() self._check_vpn_keys() # @@ -95,49 +97,21 @@ to be triggered for each one of them. self.port = port self.password = password - def _set_autostart(self): - config = self.config - if config.has_option('openvpn', 'autostart'): - autostart = config.getboolean('openvpn', - 'autostart') - self.autostart = autostart - else: - if config.has_option('DEFAULT', 'autostart'): - autostart = config.getboolean('DEFAULT', - 'autostart') - self.autostart = autostart - def _set_ovpn_command(self): - config = self.config - if config.has_option('openvpn', 'command'): - commandline = config.get('openvpn', 'command') - - command_split = commandline.split(' ') - command = command_split[0] - if len(command_split) > 1: - args = command_split[1:] - else: - args = [] - - self.command = command - self.args = args - else: - # no command in config, we build it up. # XXX check also for command-line --command flag - try: - command, args = eip_config.build_ovpn_command( - config, - debug=self.debug) - except eip_exceptions.EIPNoPolkitAuthAgentAvailable: - command = args = None - self.missing_auth_agent = True - except eip_exceptions.EIPNoPkexecAvailable: - command = args = None - self.missing_pkexec = True - - # XXX if not command, signal error. - self.command = command - self.args = args + try: + command, args = eip_config.build_ovpn_command( + debug=self.debug) + except eip_exceptions.EIPNoPolkitAuthAgentAvailable: + command = args = None + self.missing_auth_agent = True + except eip_exceptions.EIPNoPkexecAvailable: + command = args = None + self.missing_pkexec = True + + # XXX if not command, signal error. + self.command = command + self.args = args def _get_or_create_config(self): """ @@ -145,19 +119,16 @@ to be triggered for each one of them. home file, or config file passed in command line. populates command and args to be passed to subprocess. """ - config = eip_config.get_config( - config_file=self.config_file) - self.config = config - - self._set_autostart() - self._set_ovpn_command() + # XXX does nothing. + # XXX should get config? or get from checker? + pass def _check_vpn_keys(self): """ checks for correct permissions on vpn keys """ try: - eip_config.check_vpn_keys(self.config) + eip_config.check_vpn_keys() except eip_exceptions.EIPInitNoKeyFileError: self.missing_vpn_keyfile = True except eip_exceptions.EIPInitBadKeyFilePermError: -- cgit v1.2.3 From a2804c3de1470db98d8c6aa8a01e2de1aa1718a1 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 5 Sep 2012 07:42:10 +0900 Subject: app wide logging handler --- src/leap/eip/openvpnconnection.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 32fa55b1..48252e10 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -7,9 +7,7 @@ import socket import time from functools import partial -logging.basicConfig() logger = logging.getLogger(name=__name__) -logger.setLevel(logging.DEBUG) from leap.base.connection import Connection from leap.util.coroutines import spawn_and_watch_process @@ -45,6 +43,7 @@ to be triggered for each one of them. :type watcher_cb: function :type signal_map: dict """ + logger.debug('init openvpn connection') self.debug = debug #print('conductor:%s' % debug) @@ -192,7 +191,8 @@ to be triggered for each one of them. # def forget_errors(self): - print('forgetting errors') + #print('forgetting errors') + logger.debug('forgetting errors') self.with_errors = False def connect_to_management(self): -- cgit v1.2.3 From c190b7f66cc1977d0e058bfa2d8fc1a850326320 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 5 Sep 2012 10:23:24 +0900 Subject: missing_pkexec error converted to "auto" error. idea is that we define user messages in the exceptions, and queue them during (conductor) checks. user facing dialogs get constucted from exception attrs. if critical, log as such and exit. --- src/leap/eip/openvpnconnection.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 48252e10..4a6a495a 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -22,7 +22,6 @@ class OpenVPNConnection(Connection): All related to invocation of the openvpn binary """ - # Connection Methods def __init__(self, config_file=None, watcher_cb=None, @@ -45,7 +44,6 @@ to be triggered for each one of them. """ logger.debug('init openvpn connection') self.debug = debug - #print('conductor:%s' % debug) self.config_file = config_file self.watcher_cb = watcher_cb @@ -58,15 +56,18 @@ to be triggered for each one of them. self.port = None self.proto = None + ################################## # XXX move all error messages # into a more encapsulated object. self.missing_pkexec = False self.missing_auth_agent = False + self.bad_keyfile_perms = False self.missing_vpn_keyfile = False self.missing_provider = False self.missing_definition = False self.bad_provider = False + ################################# #XXX workaround for signaling #the ui that we don't know how to @@ -78,9 +79,6 @@ to be triggered for each one of them. # XXX get autostart from config self.autostart = True - #self._get_or_create_config() - self._set_ovpn_command() - self._check_vpn_keys() # # management init methods @@ -96,6 +94,11 @@ to be triggered for each one of them. self.port = port self.password = password + def run_openvpn_checks(self): + logger.debug('running openvpn checks') + self._set_ovpn_command() + self._check_vpn_keys() + def _set_ovpn_command(self): # XXX check also for command-line --command flag try: @@ -103,10 +106,13 @@ to be triggered for each one of them. debug=self.debug) except eip_exceptions.EIPNoPolkitAuthAgentAvailable: command = args = None + # XXX deprecate self.missing_auth_agent = True + raise except eip_exceptions.EIPNoPkexecAvailable: command = args = None self.missing_pkexec = True + raise # XXX if not command, signal error. self.command = command -- cgit v1.2.3 From 75f4128f5ed515c4df57275bf1479ccdf741c83f Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 6 Sep 2012 03:15:53 +0900 Subject: make tests pass. forgot to update eipconnection tests after #504 changes :( --- src/leap/eip/openvpnconnection.py | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 4a6a495a..ec8b48bf 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -57,16 +57,16 @@ to be triggered for each one of them. self.proto = None ################################## - # XXX move all error messages - # into a more encapsulated object. - self.missing_pkexec = False - self.missing_auth_agent = False - - self.bad_keyfile_perms = False - self.missing_vpn_keyfile = False - self.missing_provider = False - self.missing_definition = False - self.bad_provider = False + # This is handled by Exception attrs + # now (see #504) + #self.missing_pkexec = False + #self.missing_auth_agent = False + + #self.bad_keyfile_perms = False + #self.missing_vpn_keyfile = False + #self.missing_provider = False + #self.missing_definition = False + #self.bad_provider = False ################################# #XXX workaround for signaling @@ -107,27 +107,17 @@ to be triggered for each one of them. except eip_exceptions.EIPNoPolkitAuthAgentAvailable: command = args = None # XXX deprecate - self.missing_auth_agent = True + #self.missing_auth_agent = True raise except eip_exceptions.EIPNoPkexecAvailable: command = args = None - self.missing_pkexec = True + #self.missing_pkexec = True raise # XXX if not command, signal error. self.command = command self.args = args - def _get_or_create_config(self): - """ - retrieves the config options from defaults or - home file, or config file passed in command line. - populates command and args to be passed to subprocess. - """ - # XXX does nothing. - # XXX should get config? or get from checker? - pass - def _check_vpn_keys(self): """ checks for correct permissions on vpn keys -- cgit v1.2.3 From fc8a54a40645412e9c738723e54159bfda40cfde Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 6 Sep 2012 04:18:27 +0900 Subject: openvpn management socket is a temp path on each run --- src/leap/eip/openvpnconnection.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index ec8b48bf..2ab0622e 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -26,12 +26,10 @@ class OpenVPNConnection(Connection): def __init__(self, config_file=None, watcher_cb=None, debug=False, - host="/tmp/.eip.sock", + host=None, port="unix", password=None, *args, **kwargs): - #XXX FIXME - #change watcher_cb to line_observer """ :param config_file: configuration file to read from :param watcher_cb: callback to be \ @@ -42,8 +40,12 @@ to be triggered for each one of them. :type watcher_cb: function :type signal_map: dict """ + #XXX FIXME + #change watcher_cb to line_observer + logger.debug('init openvpn connection') self.debug = debug + # XXX if not host: raise ImproperlyConfigured self.config_file = config_file self.watcher_cb = watcher_cb @@ -103,7 +105,8 @@ to be triggered for each one of them. # XXX check also for command-line --command flag try: command, args = eip_config.build_ovpn_command( - debug=self.debug) + debug=self.debug, + socket_path=self.host) except eip_exceptions.EIPNoPolkitAuthAgentAvailable: command = args = None # XXX deprecate -- cgit v1.2.3 From 99058b9f6536a3717ab82a9d77b09d5489334eb5 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 12 Sep 2012 10:32:22 +0900 Subject: add openvpn-verb option to cli. Closes #534. accepts int [1-6] that get passed to openvpn invocation. We should filter out the polling "state"/"status" commands from the log if we want it to be real useful. --- src/leap/eip/openvpnconnection.py | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 2ab0622e..c280f70d 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -23,7 +23,8 @@ class OpenVPNConnection(Connection): of the openvpn binary """ - def __init__(self, config_file=None, + def __init__(self, + #config_file=None, watcher_cb=None, debug=False, host=None, @@ -46,8 +47,9 @@ to be triggered for each one of them. logger.debug('init openvpn connection') self.debug = debug # XXX if not host: raise ImproperlyConfigured + self.ovpn_verbosity = kwargs.get('ovpn_verbosity', None) - self.config_file = config_file + #self.config_file = config_file self.watcher_cb = watcher_cb #self.signal_maps = signal_maps @@ -58,19 +60,6 @@ to be triggered for each one of them. self.port = None self.proto = None - ################################## - # This is handled by Exception attrs - # now (see #504) - #self.missing_pkexec = False - #self.missing_auth_agent = False - - #self.bad_keyfile_perms = False - #self.missing_vpn_keyfile = False - #self.missing_provider = False - #self.missing_definition = False - #self.bad_provider = False - ################################# - #XXX workaround for signaling #the ui that we don't know how to #manage a connection error @@ -106,7 +95,8 @@ to be triggered for each one of them. try: command, args = eip_config.build_ovpn_command( debug=self.debug, - socket_path=self.host) + socket_path=self.host, + ovpn_verbosity=self.ovpn_verbosity) except eip_exceptions.EIPNoPolkitAuthAgentAvailable: command = args = None # XXX deprecate -- cgit v1.2.3 From 71cedd4e4a882765862496d77c7f04173ab4712a Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 13 Sep 2012 16:38:22 +0900 Subject: fix race condition on app init still fragile; sometimes the qt app inits faster and make the send command miss the not yet created managemente socket. --- src/leap/eip/openvpnconnection.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index c280f70d..b679a544 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -99,12 +99,9 @@ to be triggered for each one of them. ovpn_verbosity=self.ovpn_verbosity) except eip_exceptions.EIPNoPolkitAuthAgentAvailable: command = args = None - # XXX deprecate - #self.missing_auth_agent = True raise except eip_exceptions.EIPNoPkexecAvailable: command = args = None - #self.missing_pkexec = True raise # XXX if not command, signal error. @@ -159,7 +156,7 @@ to be triggered for each one of them. if self.command is None: raise eip_exceptions.EIPNoCommandError if self.subp is not None: - print('cowardly refusing to launch subprocess again') + logger.debug('cowardly refusing to launch subprocess again') return self._launch_openvpn() @@ -234,16 +231,17 @@ to be triggered for each one of them. """ Send a command to openvpn and return response as list """ - #logger.debug('connected? %s' % self.connected()) if not self.connected(): try: - #logger.debug('try to connect') self.connect_to_management() except eip_exceptions.MissingSocketError: - #XXX capture more helpful error - return self.make_error() - except: - raise + logger.warning('missing management socket') + # This should only happen briefly during + # the first invocation. Race condition make + # the polling begin before management socket + # is ready + return [] + #return self.make_error() try: if hasattr(self, 'tn'): self.tn.write(cmd + "\n") @@ -311,6 +309,7 @@ to be triggered for each one of them. """ OpenVPN command: status """ + #logger.debug('status called') status = self._send_command("status") return status -- cgit v1.2.3 From 0d35f2a82bf15504ace2135af3e0c66ae1c16874 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 18 Sep 2012 11:11:43 +0900 Subject: do_branding command added to setup --- src/leap/eip/openvpnconnection.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index c280f70d..65683485 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -117,11 +117,10 @@ to be triggered for each one of them. """ try: eip_config.check_vpn_keys() - except eip_exceptions.EIPInitNoKeyFileError: - self.missing_vpn_keyfile = True except eip_exceptions.EIPInitBadKeyFilePermError: - logger.error('error while checking vpn keys') - self.bad_keyfile_perms = True + logger.error('Bad VPN Keys permission!') + # do nothing now + # and raise the rest ... def _launch_openvpn(self): """ -- cgit v1.2.3 From f2749fa3ff1df5875d3bc0b932a408031fee9874 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 20 Sep 2012 04:39:50 +0900 Subject: toggle connection on/off --- src/leap/eip/openvpnconnection.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 92ae9de9..e32d584c 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -163,8 +163,13 @@ to be triggered for each one of them. """ terminates child subprocess """ + # XXX we should send a quit process using management + # interface. if self.subp: - self.subp.terminate() + try: + self.subp.terminate() + except OSError: + logger.error('cannot terminate subprocess!') # # management methods -- cgit v1.2.3 From 4389eed796afb58e530ac2c0d3fa0df2c5cad97f Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 20 Sep 2012 04:53:03 +0900 Subject: add logging --- src/leap/eip/openvpnconnection.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index e32d584c..f4d1c449 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -169,7 +169,8 @@ to be triggered for each one of them. try: self.subp.terminate() except OSError: - logger.error('cannot terminate subprocess!') + logger.error('cannot terminate subprocess!' + '(maybe openvpn still running?)') # # management methods -- cgit v1.2.3 From d0540e808749ff9f9e90ec5e055168f5f408e51b Mon Sep 17 00:00:00 2001 From: antialias Date: Mon, 1 Oct 2012 16:58:39 -0400 Subject: Now throws a CriticalError when an pre-exisiting openvpn istance is found. --- src/leap/eip/openvpnconnection.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index f4d1c449..a835ead9 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -3,6 +3,7 @@ OpenVPN Connection """ from __future__ import (print_function) import logging +import psutil import socket import time from functools import partial @@ -87,6 +88,7 @@ to be triggered for each one of them. def run_openvpn_checks(self): logger.debug('running openvpn checks') + self._check_if_running_instance() self._set_ovpn_command() self._check_vpn_keys() @@ -156,9 +158,20 @@ to be triggered for each one of them. raise eip_exceptions.EIPNoCommandError if self.subp is not None: logger.debug('cowardly refusing to launch subprocess again') - return + self._launch_openvpn() + def _check_if_running_instance(self): + """ + check if openvpn is already running + """ + for process in psutil.get_process_list(): + if process.name == "openvpn": + logger.debug('an openvpn instance is already running.') + raise eip_exceptions.OpenVPNAlreadyRunning + + logger.debug('no openvpn instance found.') + def cleanup(self): """ terminates child subprocess -- cgit v1.2.3 From b70a6664f0603297bf8b20809b5a64677900b405 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 11 Oct 2012 08:23:22 +0900 Subject: add signal to end of eip checks this fixes random error on leap initialization --- src/leap/eip/openvpnconnection.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index a835ead9..14839f6b 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -194,10 +194,9 @@ to be triggered for each one of them. # base class to test independently?) # - def forget_errors(self): - #print('forgetting errors') - logger.debug('forgetting errors') - self.with_errors = False + #def forget_errors(self): + #logger.debug('forgetting errors') + #self.with_errors = False def connect_to_management(self): """Connect to openvpn management interface""" @@ -216,7 +215,6 @@ to be triggered for each one of them. #self.tn.read_until('SUCCESS:', 2) self._seek_to_eof() - #self.forget_errors() return True def _seek_to_eof(self): -- cgit v1.2.3 From 0875a3d498c30187a40a788d3bd1eefa9c5924e2 Mon Sep 17 00:00:00 2001 From: antialias Date: Fri, 12 Oct 2012 15:49:28 -0400 Subject: stopping openvpn via management interface. --- src/leap/eip/openvpnconnection.py | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 14839f6b..96df4f1d 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -174,17 +174,22 @@ to be triggered for each one of them. def cleanup(self): """ - terminates child subprocess + terminates openvpn child subprocess """ - # XXX we should send a quit process using management - # interface. if self.subp: - try: - self.subp.terminate() - except OSError: - logger.error('cannot terminate subprocess!' + self._stop() + RETCODE = self.subp.wait() + if RETCODE: + logger.error('cannot terminate subprocess! ' '(maybe openvpn still running?)') + def _stop(self): + """ + stop openvpn process + """ + logger.debug("disconnecting...") + self._send_command("signal SIGTERM\n") + # # management methods # @@ -221,9 +226,16 @@ to be triggered for each one of them. """ Read as much as available. Position seek pointer to end of stream """ - b = self.tn.read_eager() - while b: + try: b = self.tn.read_eager() + except EOFError: + logger.debug("Could not read from socket. Assuming it died.") + return + while b: + try: + b = self.tn.read_eager() + except EOFError: + logger.debug("Could not read from socket. Assuming it died.") def connected(self): """ -- cgit v1.2.3 From a6c587edad293996e4015876d7e59432d6a4e8ea Mon Sep 17 00:00:00 2001 From: antialias Date: Mon, 15 Oct 2012 12:28:54 -0400 Subject: attempts to stop exisiting instances of openvpn when discovered at start up. --- src/leap/eip/openvpnconnection.py | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 96df4f1d..d93bc40f 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -168,7 +168,9 @@ to be triggered for each one of them. for process in psutil.get_process_list(): if process.name == "openvpn": logger.debug('an openvpn instance is already running.') - raise eip_exceptions.OpenVPNAlreadyRunning + logger.debug('attempting to stop openvpn instance.') + if not self._stop(): + raise eip_exceptions.OpenVPNAlreadyRunning logger.debug('no openvpn instance found.') @@ -190,7 +192,34 @@ to be triggered for each one of them. logger.debug("disconnecting...") self._send_command("signal SIGTERM\n") - # + if self.subp: + return True + + #shutting openvpn failured + #try patching in old openvpn host and trying again + process = self._get_openvpn_process() + if process: + self.host = \ + process.cmdline[process.cmdline.index("--management") + 1] + self._send_command("signal SIGTERM\n") + + #make sure the process was terminated + process = self._get_openvpn_process() + if not process: + logger.debug("Exisiting OpenVPN Process Terminated") + return True + else: + logger.error("Unable to terminate exisiting OpenVPN Process.") + return False + + return True + + def _get_openvpn_process(self): + for process in psutil.get_process_list(): + if process.name == "openvpn": + return process + return None + # management methods # # XXX REVIEW-ME -- cgit v1.2.3 From 36957e9c926f4cc56cab383d99a8f82afc4b0302 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 19 Oct 2012 01:26:41 +0900 Subject: openvpn commands cleanup --- src/leap/eip/openvpnconnection.py | 81 +++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 37 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index d93bc40f..2ec7d08c 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -64,7 +64,7 @@ to be triggered for each one of them. #XXX workaround for signaling #the ui that we don't know how to #manage a connection error - self.with_errors = False + #self.with_errors = False self.command = None self.args = None @@ -180,41 +180,22 @@ to be triggered for each one of them. """ if self.subp: self._stop() - RETCODE = self.subp.wait() - if RETCODE: - logger.error('cannot terminate subprocess! ' - '(maybe openvpn still running?)') - def _stop(self): - """ - stop openvpn process - """ - logger.debug("disconnecting...") - self._send_command("signal SIGTERM\n") - - if self.subp: - return True + # XXX kali -- + # I think this will block if child process + # does not return. + # Maybe we can .poll() for a given + # interval and exit in any case. - #shutting openvpn failured - #try patching in old openvpn host and trying again - process = self._get_openvpn_process() - if process: - self.host = \ - process.cmdline[process.cmdline.index("--management") + 1] - self._send_command("signal SIGTERM\n") - - #make sure the process was terminated - process = self._get_openvpn_process() - if not process: - logger.debug("Exisiting OpenVPN Process Terminated") - return True - else: - logger.error("Unable to terminate exisiting OpenVPN Process.") - return False - - return True + RETCODE = self.subp.wait() + if RETCODE: + logger.error( + 'cannot terminate subprocess! ' + '(We might have left openvpn running)') def _get_openvpn_process(self): + # plist = [p for p in psutil.get_process_list() if p.name == "openvpn"] + # return plist[0] if plist else None for process in psutil.get_process_list(): if process.name == "openvpn": return process @@ -293,12 +274,7 @@ to be triggered for each one of them. self.connect_to_management() except eip_exceptions.MissingSocketError: logger.warning('missing management socket') - # This should only happen briefly during - # the first invocation. Race condition make - # the polling begin before management socket - # is ready return [] - #return self.make_error() try: if hasattr(self, 'tn'): self.tn.write(cmd + "\n") @@ -376,6 +352,37 @@ to be triggered for each one of them. """ return self._send_command("status 2") + def _stop(self): + """ + stop openvpn process + by sending SIGTERM to the management + interface + """ + logger.debug("disconnecting...") + self._send_command("signal SIGTERM\n") + + if self.subp: + return True + + #shutting openvpn failured + #try patching in old openvpn host and trying again + process = self._get_openvpn_process() + if process: + self.host = \ + process.cmdline[process.cmdline.index("--management") + 1] + self._send_command("signal SIGTERM\n") + + #make sure the process was terminated + process = self._get_openvpn_process() + if not process: + logger.debug("Existing OpenVPN Process Terminated") + return True + else: + logger.error("Unable to terminate existing OpenVPN Process.") + return False + + return True + # # parse info # -- cgit v1.2.3 From 0060d3c74adce19fab7215b3788c5197cc05a9ae Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 24 Oct 2012 04:05:19 +0900 Subject: sign up branch ends by triggering eip connection still need to bind signals properly, and block on the validation process until we receive the "connected" signal. but the basic flow is working again, i.e, user should be able to remove the .config/leap folder and get all the needed info from the provider. --- src/leap/eip/openvpnconnection.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 2ec7d08c..d7c571bc 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -25,7 +25,6 @@ class OpenVPNConnection(Connection): """ def __init__(self, - #config_file=None, watcher_cb=None, debug=False, host=None, @@ -96,6 +95,7 @@ to be triggered for each one of them. # XXX check also for command-line --command flag try: command, args = eip_config.build_ovpn_command( + provider=self.provider, debug=self.debug, socket_path=self.host, ovpn_verbosity=self.ovpn_verbosity) @@ -115,7 +115,7 @@ to be triggered for each one of them. checks for correct permissions on vpn keys """ try: - eip_config.check_vpn_keys() + eip_config.check_vpn_keys(provider=self.provider) except eip_exceptions.EIPInitBadKeyFilePermError: logger.error('Bad VPN Keys permission!') # do nothing now -- cgit v1.2.3 From 50ae1a415698af8aaa2fbed186a9f05037a9bfd9 Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 12 Nov 2012 22:00:58 +0900 Subject: catch error when management interface is missing during shutdown --- src/leap/eip/openvpnconnection.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index d7c571bc..34f1e18b 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -179,7 +179,12 @@ to be triggered for each one of them. terminates openvpn child subprocess """ if self.subp: - self._stop() + try: + self._stop() + except eip_exceptions.ConnectionRefusedError: + logger.warning( + 'unable to send sigterm signal to openvpn: ' + 'connection refused.') # XXX kali -- # I think this will block if child process @@ -190,8 +195,8 @@ to be triggered for each one of them. RETCODE = self.subp.wait() if RETCODE: logger.error( - 'cannot terminate subprocess! ' - '(We might have left openvpn running)') + 'cannot terminate subprocess! Retcode %s' + '(We might have left openvpn running)' % RETCODE) def _get_openvpn_process(self): # plist = [p for p in psutil.get_process_list() if p.name == "openvpn"] -- cgit v1.2.3 From d24c7328fa845737dbb83d512e4b3f287634c4cc Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 14 Nov 2012 00:33:05 +0900 Subject: make tests pass + pep8 They were breaking mainly because I did not bother to have a pass over them to change the PROVIDER settings from the branding case. All good now, although much testing is yet needed and some refactor could be used. long live green tests! --- src/leap/eip/openvpnconnection.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 34f1e18b..4104bd0e 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -233,8 +233,8 @@ to be triggered for each one of them. #self.tn.read_until('ENTER PASSWORD:', 2) #self.tn.write(self.password + '\n') #self.tn.read_until('SUCCESS:', 2) - - self._seek_to_eof() + if self.tn: + self._seek_to_eof() return True def _seek_to_eof(self): @@ -364,7 +364,8 @@ to be triggered for each one of them. interface """ logger.debug("disconnecting...") - self._send_command("signal SIGTERM\n") + if self.connected(): + self._send_command("signal SIGTERM\n") if self.subp: return True @@ -373,9 +374,13 @@ to be triggered for each one of them. #try patching in old openvpn host and trying again process = self._get_openvpn_process() if process: - self.host = \ - process.cmdline[process.cmdline.index("--management") + 1] - self._send_command("signal SIGTERM\n") + logger.debug('process :%s' % process) + cmdline = process.cmdline + + if isinstance(cmdline, list): + _index = cmdline.index("--management") + self.host = cmdline[_index + 1] + self._send_command("signal SIGTERM\n") #make sure the process was terminated process = self._get_openvpn_process() -- cgit v1.2.3 From 834b04317535cb6d18c02e3aa4d37b09fcf99868 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 14 Nov 2012 03:39:14 +0900 Subject: delete temp folder for management socket after client shutdown Close #742 --- src/leap/eip/openvpnconnection.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 4104bd0e..45a1847c 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -3,7 +3,9 @@ OpenVPN Connection """ from __future__ import (print_function) import logging +import os import psutil +import shutil import socket import time from functools import partial @@ -187,6 +189,7 @@ to be triggered for each one of them. 'connection refused.') # XXX kali -- + # XXX review-me # I think this will block if child process # does not return. # Maybe we can .poll() for a given @@ -198,6 +201,26 @@ to be triggered for each one of them. 'cannot terminate subprocess! Retcode %s' '(We might have left openvpn running)' % RETCODE) + self.cleanup_tempfiles() + + def cleanup_tempfiles(self): + """ + remove all temporal files + we might have left behind + """ + # if self.port is 'unix', we have + # created a temporal socket path that, under + # normal circumstances, we should be able to + # delete + + if self.port == "unix": + # I'm tempted to catch a generic exception here, + # but I prefer to let it crash so we can catch + # specific errors that right now I'm not able + # to think of. + logger.debug('cleaning socket file temp folder') + shutil.rmtree(os.path.split(self.host)[0]) + def _get_openvpn_process(self): # plist = [p for p in psutil.get_process_list() if p.name == "openvpn"] # return plist[0] if plist else None -- cgit v1.2.3 From 862014f68fce37318f77309a8f8f9782dabc60d2 Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 19 Nov 2012 14:09:24 +0900 Subject: fix delete temporal files --- src/leap/eip/openvpnconnection.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 45a1847c..85874cfd 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -214,12 +214,14 @@ to be triggered for each one of them. # delete if self.port == "unix": - # I'm tempted to catch a generic exception here, - # but I prefer to let it crash so we can catch - # specific errors that right now I'm not able - # to think of. logger.debug('cleaning socket file temp folder') - shutil.rmtree(os.path.split(self.host)[0]) + + tempfolder = os.path.split(self.host)[0] + if os.path.isdir(tempfolder): + try: + shutil.rmtree(tempfolder) + except OSError: + logger.error('could not delete tmpfolder %s' % tempfolder) def _get_openvpn_process(self): # plist = [p for p in psutil.get_process_list() if p.name == "openvpn"] -- cgit v1.2.3 From a3ce61ea54b0b0f5c1ecd5904379e27cfec885b5 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 28 Nov 2012 02:43:25 +0900 Subject: call shutdown signal from sigint_handler --- src/leap/eip/openvpnconnection.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 85874cfd..859378c0 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -390,9 +390,14 @@ to be triggered for each one of them. """ logger.debug("disconnecting...") if self.connected(): - self._send_command("signal SIGTERM\n") + try: + self._send_command("signal SIGTERM\n") + except socket.error: + logger.warning('management socket died') + return if self.subp: + # ??? return True #shutting openvpn failured -- cgit v1.2.3 From cd78d9d552977e8f8fb12b6a2ff56fda9c37bf35 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 7 Dec 2012 05:32:50 +0900 Subject: only remove management socket when shutting down Closes #1090 --- src/leap/eip/openvpnconnection.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 859378c0..07bc628a 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -176,7 +176,7 @@ to be triggered for each one of them. logger.debug('no openvpn instance found.') - def cleanup(self): + def cleanup(self, shutdown=False): """ terminates openvpn child subprocess """ @@ -201,7 +201,8 @@ to be triggered for each one of them. 'cannot terminate subprocess! Retcode %s' '(We might have left openvpn running)' % RETCODE) - self.cleanup_tempfiles() + if shutdown: + self.cleanup_tempfiles() def cleanup_tempfiles(self): """ -- cgit v1.2.3 From 38cc1758240a3c64db387b0437dcf1517b52da15 Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 10 Dec 2012 19:51:53 +0900 Subject: cleanup and rewrite eipconnection/openvpnconnection classes --- src/leap/eip/openvpnconnection.py | 472 ++++++++++++++++---------------------- 1 file changed, 203 insertions(+), 269 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 07bc628a..253f5056 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -7,7 +7,6 @@ import os import psutil import shutil import socket -import time from functools import partial logger = logging.getLogger(name=__name__) @@ -20,12 +19,123 @@ from leap.eip import config as eip_config from leap.eip import exceptions as eip_exceptions -class OpenVPNConnection(Connection): +class OpenVPNManagement(object): + + # TODO explain a little bit how management interface works + # and our telnet interface with support for unix sockets. + + """ + for more information, read openvpn management notes. + zcat `dpkg -L openvpn | grep management` + """ + + def _connect_to_management(self): + """ + Connect to openvpn management interface + """ + if hasattr(self, 'tn'): + self._close_management_socket() + self.tn = UDSTelnet(self.host, self.port) + + # XXX make password optional + # specially for win. we should generate + # the pass on the fly when invoking manager + # from conductor + + #self.tn.read_until('ENTER PASSWORD:', 2) + #self.tn.write(self.password + '\n') + #self.tn.read_until('SUCCESS:', 2) + if self.tn: + self._seek_to_eof() + return True + + def _close_management_socket(self, announce=True): + """ + Close connection to openvpn management interface + """ + logger.debug('closing socket') + if announce: + self.tn.write("quit\n") + self.tn.read_all() + self.tn.get_socket().close() + del self.tn + + def _seek_to_eof(self): + """ + Read as much as available. Position seek pointer to end of stream + """ + try: + b = self.tn.read_eager() + except EOFError: + logger.debug("Could not read from socket. Assuming it died.") + return + while b: + try: + b = self.tn.read_eager() + except EOFError: + logger.debug("Could not read from socket. Assuming it died.") + + def _send_command(self, cmd): + """ + Send a command to openvpn and return response as list + """ + if not self.connected(): + try: + self._connect_to_management() + except eip_exceptions.MissingSocketError: + logger.warning('missing management socket') + return [] + try: + if hasattr(self, 'tn'): + self.tn.write(cmd + "\n") + except socket.error: + logger.error('socket error') + self._close_management_socket(announce=False) + return [] + buf = self.tn.read_until(b"END", 2) + self._seek_to_eof() + blist = buf.split('\r\n') + if blist[-1].startswith('END'): + del blist[-1] + return blist + else: + return [] + + def _send_short_command(self, cmd): + """ + parse output from commands that are + delimited by "success" instead + """ + if not self.connected(): + self.connect() + self.tn.write(cmd + "\n") + # XXX not working? + buf = self.tn.read_until(b"SUCCESS", 2) + self._seek_to_eof() + blist = buf.split('\r\n') + return blist + + # + # random maybe useful vpn commands + # + + def pid(self): + #XXX broken + return self._send_short_command("pid") + + +class OpenVPNConnection(Connection, OpenVPNManagement): """ All related to invocation - of the openvpn binary + of the openvpn binary. + It's extended by EIPConnection. """ + # XXX Inheriting from Connection was an early design idea + # but currently that's an empty class. + # We can get rid of that if we don't use it for sharing + # state with other leap modules. + def __init__(self, watcher_cb=None, debug=False, @@ -34,24 +144,21 @@ class OpenVPNConnection(Connection): password=None, *args, **kwargs): """ - :param config_file: configuration file to read from :param watcher_cb: callback to be \ called for each line in watched stdout :param signal_map: dictionary of signal names and callables \ to be triggered for each one of them. - :type config_file: str :type watcher_cb: function :type signal_map: dict """ #XXX FIXME #change watcher_cb to line_observer + # XXX if not host: raise ImproperlyConfigured logger.debug('init openvpn connection') self.debug = debug - # XXX if not host: raise ImproperlyConfigured self.ovpn_verbosity = kwargs.get('ovpn_verbosity', None) - #self.config_file = config_file self.watcher_cb = watcher_cb #self.signal_maps = signal_maps @@ -62,21 +169,13 @@ to be triggered for each one of them. self.port = None self.proto = None - #XXX workaround for signaling - #the ui that we don't know how to - #manage a connection error - #self.with_errors = False - self.command = None self.args = None # XXX get autostart from config self.autostart = True - # - # management init methods - # - + # management interface init self.host = host if isinstance(port, str) and port.isdigit(): port = int(port) @@ -88,101 +187,47 @@ to be triggered for each one of them. self.password = password def run_openvpn_checks(self): + """ + runs check needed before launching + openvpn subprocess. will raise if errors found. + """ logger.debug('running openvpn checks') + # XXX I think that "check_if_running" should be called + # from try openvpn connection instead. -- kali. + # let's prepare tests for that before changing it... self._check_if_running_instance() self._set_ovpn_command() self._check_vpn_keys() - def _set_ovpn_command(self): - # XXX check also for command-line --command flag - try: - command, args = eip_config.build_ovpn_command( - provider=self.provider, - debug=self.debug, - socket_path=self.host, - ovpn_verbosity=self.ovpn_verbosity) - except eip_exceptions.EIPNoPolkitAuthAgentAvailable: - command = args = None - raise - except eip_exceptions.EIPNoPkexecAvailable: - command = args = None - raise - - # XXX if not command, signal error. - self.command = command - self.args = args - - def _check_vpn_keys(self): - """ - checks for correct permissions on vpn keys - """ - try: - eip_config.check_vpn_keys(provider=self.provider) - except eip_exceptions.EIPInitBadKeyFilePermError: - logger.error('Bad VPN Keys permission!') - # do nothing now - # and raise the rest ... - - def _launch_openvpn(self): - """ - invocation of openvpn binaries in a subprocess. - """ - #XXX TODO: - #deprecate watcher_cb, - #use _only_ signal_maps instead - - logger.debug('_launch_openvpn called') - if self.watcher_cb is not None: - linewrite_callback = self.watcher_cb - else: - #XXX get logger instead - linewrite_callback = lambda line: print('watcher: %s' % line) - - # the partial is not - # being applied now because we're not observing the process - # stdout like we did in the early stages. but I leave it - # here since it will be handy for observing patterns in the - # thru-the-manager updates (with regex) - observers = (linewrite_callback, - partial(lambda con_status, line: None, self.status)) - subp, watcher = spawn_and_watch_process( - self.command, - self.args, - observers=observers) - self.subp = subp - self.watcher = watcher - - def _try_connection(self): + def try_openvpn_connection(self): """ attempts to connect """ + # XXX should make public method if self.command is None: raise eip_exceptions.EIPNoCommandError if self.subp is not None: logger.debug('cowardly refusing to launch subprocess again') + # XXX this is not returning ???!! + # FIXME -- so it's calling it all the same!! self._launch_openvpn() - def _check_if_running_instance(self): + def connected(self): """ - check if openvpn is already running + Returns True if connected + rtype: bool """ - for process in psutil.get_process_list(): - if process.name == "openvpn": - logger.debug('an openvpn instance is already running.') - logger.debug('attempting to stop openvpn instance.') - if not self._stop(): - raise eip_exceptions.OpenVPNAlreadyRunning - - logger.debug('no openvpn instance found.') + # XXX make a property + return hasattr(self, 'tn') - def cleanup(self, shutdown=False): + def terminate_openvpn_connection(self, shutdown=False): """ terminates openvpn child subprocess """ if self.subp: try: - self._stop() + self._stop_openvpn() except eip_exceptions.ConnectionRefusedError: logger.warning( 'unable to send sigterm signal to openvpn: ' @@ -202,9 +247,9 @@ to be triggered for each one of them. '(We might have left openvpn running)' % RETCODE) if shutdown: - self.cleanup_tempfiles() + self._cleanup_tempfiles() - def cleanup_tempfiles(self): + def _cleanup_tempfiles(self): """ remove all temporal files we might have left behind @@ -224,172 +269,89 @@ to be triggered for each one of them. except OSError: logger.error('could not delete tmpfolder %s' % tempfolder) - def _get_openvpn_process(self): - # plist = [p for p in psutil.get_process_list() if p.name == "openvpn"] - # return plist[0] if plist else None + # checks + + def _check_if_running_instance(self): + """ + check if openvpn is already running + """ for process in psutil.get_process_list(): if process.name == "openvpn": - return process - return None - - # management methods - # - # XXX REVIEW-ME - # REFACTOR INFO: (former "manager". - # Can we move to another - # base class to test independently?) - # - - #def forget_errors(self): - #logger.debug('forgetting errors') - #self.with_errors = False - - def connect_to_management(self): - """Connect to openvpn management interface""" - #logger.debug('connecting socket') - if hasattr(self, 'tn'): - self.close() - self.tn = UDSTelnet(self.host, self.port) - - # XXX make password optional - # specially for win. we should generate - # the pass on the fly when invoking manager - # from conductor + logger.debug('an openvpn instance is already running.') + logger.debug('attempting to stop openvpn instance.') + if not self._stop_openvpn(): + raise eip_exceptions.OpenVPNAlreadyRunning - #self.tn.read_until('ENTER PASSWORD:', 2) - #self.tn.write(self.password + '\n') - #self.tn.read_until('SUCCESS:', 2) - if self.tn: - self._seek_to_eof() - return True + logger.debug('no openvpn instance found.') - def _seek_to_eof(self): - """ - Read as much as available. Position seek pointer to end of stream - """ + def _set_ovpn_command(self): try: - b = self.tn.read_eager() - except EOFError: - logger.debug("Could not read from socket. Assuming it died.") - return - while b: - try: - b = self.tn.read_eager() - except EOFError: - logger.debug("Could not read from socket. Assuming it died.") - - def connected(self): - """ - Returns True if connected - rtype: bool - """ - return hasattr(self, 'tn') + command, args = eip_config.build_ovpn_command( + provider=self.provider, + debug=self.debug, + socket_path=self.host, + ovpn_verbosity=self.ovpn_verbosity) + except eip_exceptions.EIPNoPolkitAuthAgentAvailable: + command = args = None + raise + except eip_exceptions.EIPNoPkexecAvailable: + command = args = None + raise - def close(self, announce=True): - """ - Close connection to openvpn management interface - """ - logger.debug('closing socket') - if announce: - self.tn.write("quit\n") - self.tn.read_all() - self.tn.get_socket().close() - del self.tn + # XXX if not command, signal error. + self.command = command + self.args = args - def _send_command(self, cmd): + def _check_vpn_keys(self): """ - Send a command to openvpn and return response as list + checks for correct permissions on vpn keys """ - if not self.connected(): - try: - self.connect_to_management() - except eip_exceptions.MissingSocketError: - logger.warning('missing management socket') - return [] try: - if hasattr(self, 'tn'): - self.tn.write(cmd + "\n") - except socket.error: - logger.error('socket error') - self.close(announce=False) - return [] - buf = self.tn.read_until(b"END", 2) - self._seek_to_eof() - blist = buf.split('\r\n') - if blist[-1].startswith('END'): - del blist[-1] - return blist - else: - return [] - - def _send_short_command(self, cmd): - """ - parse output from commands that are - delimited by "success" instead - """ - if not self.connected(): - self.connect() - self.tn.write(cmd + "\n") - # XXX not working? - buf = self.tn.read_until(b"SUCCESS", 2) - self._seek_to_eof() - blist = buf.split('\r\n') - return blist - - # - # useful vpn commands - # - - def pid(self): - #XXX broken - return self._send_short_command("pid") + eip_config.check_vpn_keys(provider=self.provider) + except eip_exceptions.EIPInitBadKeyFilePermError: + logger.error('Bad VPN Keys permission!') + # do nothing now + # and raise the rest ... - def make_error(self): - """ - capture error and wrap it in an - understandable format - """ - #XXX get helpful error codes - self.with_errors = True - now = int(time.time()) - return '%s,LAUNCHER ERROR,ERROR,-,-' % now + # starting and stopping openvpn subprocess - def state(self): + def _launch_openvpn(self): """ - OpenVPN command: state + invocation of openvpn binaries in a subprocess. """ - state = self._send_command("state") - if not state: - return None - if isinstance(state, str): - return state - if isinstance(state, list): - if len(state) == 1: - return state[0] - else: - return state[-1] + #XXX TODO: + #deprecate watcher_cb, + #use _only_ signal_maps instead - def vpn_status(self): - """ - OpenVPN command: status - """ - #logger.debug('status called') - status = self._send_command("status") - return status + logger.debug('_launch_openvpn called') + if self.watcher_cb is not None: + linewrite_callback = self.watcher_cb + else: + #XXX get logger instead + linewrite_callback = lambda line: print('watcher: %s' % line) - def vpn_status2(self): - """ - OpenVPN command: last 2 statuses - """ - return self._send_command("status 2") + # the partial is not + # being applied now because we're not observing the process + # stdout like we did in the early stages. but I leave it + # here since it will be handy for observing patterns in the + # thru-the-manager updates (with regex) + observers = (linewrite_callback, + partial(lambda con_status, line: None, self.status)) + subp, watcher = spawn_and_watch_process( + self.command, + self.args, + observers=observers) + self.subp = subp + self.watcher = watcher - def _stop(self): + def _stop_openvpn(self): """ stop openvpn process by sending SIGTERM to the management interface """ - logger.debug("disconnecting...") + # XXX method a bit too long, split + logger.debug("terminating openvpn process...") if self.connected(): try: self._send_command("signal SIGTERM\n") @@ -424,38 +386,10 @@ to be triggered for each one of them. return True - # - # parse info - # - - def get_status_io(self): - status = self.vpn_status() - if isinstance(status, str): - lines = status.split('\n') - if isinstance(status, list): - lines = status - try: - (header, when, tun_read, tun_write, - tcp_read, tcp_write, auth_read) = tuple(lines) - except ValueError: - return None - - when_ts = time.strptime(when.split(',')[1], "%a %b %d %H:%M:%S %Y") - sep = ',' - # XXX cleanup! - tun_read = tun_read.split(sep)[1] - tun_write = tun_write.split(sep)[1] - tcp_read = tcp_read.split(sep)[1] - tcp_write = tcp_write.split(sep)[1] - auth_read = auth_read.split(sep)[1] - - # XXX this could be a named tuple. prettier. - return when_ts, (tun_read, tun_write, tcp_read, tcp_write, auth_read) - - def get_connection_state(self): - state = self.state() - if state is not None: - ts, status_step, ok, ip, remote = state.split(',') - ts = time.gmtime(float(ts)) - # XXX this could be a named tuple. prettier. - return ts, status_step, ok, ip, remote + def _get_openvpn_process(self): + # plist = [p for p in psutil.get_process_list() if p.name == "openvpn"] + # return plist[0] if plist else None + for process in psutil.get_process_list(): + if process.name == "openvpn": + return process + return None -- cgit v1.2.3 From 75057e55828accb62beef1f73364edd82a75ed30 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 18 Dec 2012 05:54:50 +0900 Subject: do not pass CA.crt to checks for https also skip temporary errors when previous openvpn process vanishes. --- src/leap/eip/openvpnconnection.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 253f5056..59ba44f0 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -275,12 +275,16 @@ to be triggered for each one of them. """ check if openvpn is already running """ - for process in psutil.get_process_list(): - if process.name == "openvpn": - logger.debug('an openvpn instance is already running.') - logger.debug('attempting to stop openvpn instance.') - if not self._stop_openvpn(): - raise eip_exceptions.OpenVPNAlreadyRunning + try: + for process in psutil.get_process_list(): + if process.name == "openvpn": + logger.debug('an openvpn instance is already running.') + logger.debug('attempting to stop openvpn instance.') + if not self._stop_openvpn(): + raise eip_exceptions.OpenVPNAlreadyRunning + + except psutil.error.NoSuchProcess: + logger.debug('detected a process which died. passing.') logger.debug('no openvpn instance found.') -- cgit v1.2.3 From 8808c0e2cba1660515fab4a2931221c3bda7e093 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 18 Dec 2012 20:59:51 +0900 Subject: do not assume that we were the only ones launching openvpn --- src/leap/eip/openvpnconnection.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 59ba44f0..c2dc71a6 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -374,8 +374,9 @@ to be triggered for each one of them. logger.debug('process :%s' % process) cmdline = process.cmdline - if isinstance(cmdline, list): - _index = cmdline.index("--management") + manag_flag = "--management" + if isinstance(cmdline, list) and manag_flag in cmdline: + _index = cmdline.index(manag_flag) self.host = cmdline[_index + 1] self._send_command("signal SIGTERM\n") -- cgit v1.2.3 From 656419216f15bfb1859ba850d2d9c9d143034e23 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 8 Jan 2013 00:32:21 +0900 Subject: doc polishing --- src/leap/eip/openvpnconnection.py | 1 + 1 file changed, 1 insertion(+) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index c2dc71a6..eb3b5ec0 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -276,6 +276,7 @@ to be triggered for each one of them. check if openvpn is already running """ try: + #FIXME this gives DeprecationWarning for process in psutil.get_process_list(): if process.name == "openvpn": logger.debug('an openvpn instance is already running.') -- cgit v1.2.3 From e35eb606faef1ccd06201a0b38a462375426cedd Mon Sep 17 00:00:00 2001 From: kali Date: Mon, 7 Jan 2013 21:10:41 +0900 Subject: Working OSX installer workflow. Using platypus for installer. Working installer at 17.6MB compressed. --- src/leap/eip/openvpnconnection.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index eb3b5ec0..b36b0b16 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -83,7 +83,7 @@ class OpenVPNManagement(object): try: self._connect_to_management() except eip_exceptions.MissingSocketError: - logger.warning('missing management socket') + #logger.warning('missing management socket') return [] try: if hasattr(self, 'tn'): @@ -329,11 +329,12 @@ to be triggered for each one of them. #use _only_ signal_maps instead logger.debug('_launch_openvpn called') + logger.debug('watcher_cb: %s' % self.watcher_cb) if self.watcher_cb is not None: linewrite_callback = self.watcher_cb else: #XXX get logger instead - linewrite_callback = lambda line: print('watcher: %s' % line) + linewrite_callback = lambda line: logger.debug('watcher: %s' % line) # the partial is not # being applied now because we're not observing the process @@ -341,7 +342,7 @@ to be triggered for each one of them. # here since it will be handy for observing patterns in the # thru-the-manager updates (with regex) observers = (linewrite_callback, - partial(lambda con_status, line: None, self.status)) + partial(lambda con_status, line: linewrite_callback, self.status)) subp, watcher = spawn_and_watch_process( self.command, self.args, -- cgit v1.2.3 From 348eb0852d6f1b8b2b72baba8a236bc30a6f2a4e Mon Sep 17 00:00:00 2001 From: antialias Date: Fri, 16 Nov 2012 17:38:46 -0800 Subject: reads and searches for strings from openvpn logs via the management interface. --- src/leap/eip/openvpnconnection.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index b36b0b16..233b9da3 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -400,3 +400,7 @@ to be triggered for each one of them. if process.name == "openvpn": return process return None + + def get_log(self, lines=1): + log = self._send_command("log %s" % lines) + return log -- cgit v1.2.3 From bf39c45eddc62733fdb72b4f46cdb81ec649cb30 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 16 Jan 2013 00:58:22 +0900 Subject: handle loss of tun iface trigger only one dialog and disconnect. additional cleanup of log handling. --- src/leap/eip/openvpnconnection.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 233b9da3..a36d99de 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -92,14 +92,17 @@ class OpenVPNManagement(object): logger.error('socket error') self._close_management_socket(announce=False) return [] - buf = self.tn.read_until(b"END", 2) - self._seek_to_eof() - blist = buf.split('\r\n') - if blist[-1].startswith('END'): - del blist[-1] - return blist - else: - return [] + try: + buf = self.tn.read_until(b"END", 2) + self._seek_to_eof() + blist = buf.split('\r\n') + if blist[-1].startswith('END'): + del blist[-1] + return blist + else: + return [] + except socket.error as exc: + logger.debug('socket error: %s' % exc.message) def _send_short_command(self, cmd): """ @@ -329,12 +332,12 @@ to be triggered for each one of them. #use _only_ signal_maps instead logger.debug('_launch_openvpn called') - logger.debug('watcher_cb: %s' % self.watcher_cb) if self.watcher_cb is not None: linewrite_callback = self.watcher_cb else: #XXX get logger instead - linewrite_callback = lambda line: logger.debug('watcher: %s' % line) + linewrite_callback = lambda line: logger.debug( + 'watcher: %s' % line) # the partial is not # being applied now because we're not observing the process @@ -342,7 +345,8 @@ to be triggered for each one of them. # here since it will be handy for observing patterns in the # thru-the-manager updates (with regex) observers = (linewrite_callback, - partial(lambda con_status, line: linewrite_callback, self.status)) + partial(lambda con_status, + line: linewrite_callback, self.status)) subp, watcher = spawn_and_watch_process( self.command, self.args, -- cgit v1.2.3 From d6c8cb0f12e8924820c296a8114a7899f61e5180 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 17 Jan 2013 05:54:16 +0900 Subject: (osx) detect which interface is traffic going thru --- src/leap/eip/openvpnconnection.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index a36d99de..e5169465 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -6,6 +6,7 @@ import logging import os import psutil import shutil +import select import socket from functools import partial @@ -103,6 +104,8 @@ class OpenVPNManagement(object): return [] except socket.error as exc: logger.debug('socket error: %s' % exc.message) + except select.error as exc: + logger.debug('select error: %s' % exc.message) def _send_short_command(self, cmd): """ -- cgit v1.2.3 From 6e9c63f47b98fbfcd3a5104fbfa5cc9d9ffe5143 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 17 Jan 2013 07:31:59 +0900 Subject: osx fixed already running instance check --- src/leap/eip/openvpnconnection.py | 47 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 24 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index e5169465..05979ff7 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -2,18 +2,21 @@ OpenVPN Connection """ from __future__ import (print_function) +from functools import partial import logging import os import psutil import shutil import select import socket -from functools import partial +from time import sleep logger = logging.getLogger(name=__name__) from leap.base.connection import Connection +from leap.base.constants import OPENVPN_BIN from leap.util.coroutines import spawn_and_watch_process +from leap.util.misc import get_openvpn_pids from leap.eip.udstelnet import UDSTelnet from leap.eip import config as eip_config @@ -277,23 +280,20 @@ to be triggered for each one of them. # checks + def _check_if_running_instance(self): """ check if openvpn is already running """ - try: - #FIXME this gives DeprecationWarning - for process in psutil.get_process_list(): - if process.name == "openvpn": - logger.debug('an openvpn instance is already running.') - logger.debug('attempting to stop openvpn instance.') - if not self._stop_openvpn(): - raise eip_exceptions.OpenVPNAlreadyRunning - - except psutil.error.NoSuchProcess: - logger.debug('detected a process which died. passing.') - - logger.debug('no openvpn instance found.') + openvpn_pids = get_openvpn_pids() + if openvpn_pids: + logger.debug('an openvpn instance is already running.') + logger.debug('attempting to stop openvpn instance.') + if not self._stop_openvpn(): + raise eip_exceptions.OpenVPNAlreadyRunning + return + else: + logger.debug('no openvpn instance found.') def _set_ovpn_command(self): try: @@ -334,7 +334,7 @@ to be triggered for each one of them. #deprecate watcher_cb, #use _only_ signal_maps instead - logger.debug('_launch_openvpn called') + #logger.debug('_launch_openvpn called') if self.watcher_cb is not None: linewrite_callback = self.watcher_cb else: @@ -364,23 +364,24 @@ to be triggered for each one of them. interface """ # XXX method a bit too long, split - logger.debug("terminating openvpn process...") + logger.debug("atempting to terminate openvpn process...") if self.connected(): try: self._send_command("signal SIGTERM\n") + sleep(1) + if not self.subp: # XXX ??? + return True except socket.error: logger.warning('management socket died') return - if self.subp: - # ??? - return True #shutting openvpn failured #try patching in old openvpn host and trying again + # XXX could be more than one! process = self._get_openvpn_process() if process: - logger.debug('process :%s' % process) + logger.debug('process: %s' % process.name) cmdline = process.cmdline manag_flag = "--management" @@ -401,10 +402,8 @@ to be triggered for each one of them. return True def _get_openvpn_process(self): - # plist = [p for p in psutil.get_process_list() if p.name == "openvpn"] - # return plist[0] if plist else None - for process in psutil.get_process_list(): - if process.name == "openvpn": + for process in psutil.process_iter(): + if OPENVPN_BIN in process.name: return process return None -- cgit v1.2.3 From 407b030bb7d27b797fb27254710a358c9c69f8be Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 24 Jan 2013 01:57:28 +0900 Subject: catch missing messages on last page of wizard --- src/leap/eip/openvpnconnection.py | 1 - 1 file changed, 1 deletion(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 05979ff7..4953db11 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -280,7 +280,6 @@ to be triggered for each one of them. # checks - def _check_if_running_instance(self): """ check if openvpn is already running -- cgit v1.2.3 From 9cdc193c587631986e579c1ba37a8b982be01238 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 24 Jan 2013 18:47:41 +0900 Subject: all tests green again plus: * added soledad test requirements * removed soledad from run_tests run (+1K tests failing) * added option to run All tests to run_tests script * pep8 cleanup --- src/leap/eip/openvpnconnection.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 4953db11..455735c8 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -107,7 +107,7 @@ class OpenVPNManagement(object): return [] except socket.error as exc: logger.debug('socket error: %s' % exc.message) - except select.error as exc: + except select.error as exc: logger.debug('select error: %s' % exc.message) def _send_short_command(self, cmd): @@ -374,7 +374,6 @@ to be triggered for each one of them. logger.warning('management socket died') return - #shutting openvpn failured #try patching in old openvpn host and trying again # XXX could be more than one! -- cgit v1.2.3 From 1e9ba29d0f1e12b95536099e29396a1f35908381 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 31 Jan 2013 05:47:45 +0900 Subject: pep8 --- src/leap/eip/openvpnconnection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/eip/openvpnconnection.py') diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index 455735c8..bee8c010 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -339,7 +339,7 @@ to be triggered for each one of them. else: #XXX get logger instead linewrite_callback = lambda line: logger.debug( - 'watcher: %s' % line) + 'watcher: %s' % line) # the partial is not # being applied now because we're not observing the process -- cgit v1.2.3