From a58b77ca9cd3620201306258dd6bd05ea1c73c73 Mon Sep 17 00:00:00 2001 From: Nathan Kinder Date: Wed, 25 Feb 2015 15:19:47 -0800 Subject: Timeout when performing time sync during client install We use ntpd now to sync time before fetching a TGT during client install. Unfortuantely, ntpd will hang forever if it is unable to reach the NTP server. This patch adds the ability for commands run via ipautil.run() to have an optional timeout. This capability is used by the NTP sync code that is run during ipa-client-install. Ticket: https://fedorahosted.org/freeipa/ticket/4842 Reviewed-By: Martin Babinsky --- ipa-client/ipaclient/ntpconf.py | 8 +++++++- ipaplatform/base/paths.py | 1 + ipapython/ipautil.py | 12 +++++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/ipa-client/ipaclient/ntpconf.py b/ipa-client/ipaclient/ntpconf.py index e1ac55a1d..7d5c82a89 100644 --- a/ipa-client/ipaclient/ntpconf.py +++ b/ipa-client/ipaclient/ntpconf.py @@ -18,6 +18,7 @@ # from ipapython import ipautil +from ipapython.ipa_log_manager import root_logger import shutil import os from ipaplatform.tasks import tasks @@ -149,7 +150,12 @@ def synconce_ntp(server_fqdn): tmp_ntp_conf = ipautil.write_tmp_file('server %s' % server_fqdn) try: - ipautil.run([ntpd, '-qgc', tmp_ntp_conf.name]) + # The ntpd command will never exit if it is unable to reach the + # server, so timeout after 15 seconds. + timeout = 15 + root_logger.info('Attempting to sync time using ntpd. ' + 'Will timeout after %d seconds' % timeout) + ipautil.run([ntpd, '-qgc', tmp_ntp_conf.name], timeout=timeout) return True except ipautil.CalledProcessError: return False diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py index 7922e3bbc..11c7e9212 100644 --- a/ipaplatform/base/paths.py +++ b/ipaplatform/base/paths.py @@ -186,6 +186,7 @@ class BasePathNamespace(object): SSLGET = "/usr/bin/sslget" SSS_SSH_AUTHORIZEDKEYS = "/usr/bin/sss_ssh_authorizedkeys" SSS_SSH_KNOWNHOSTSPROXY = "/usr/bin/sss_ssh_knownhostsproxy" + BIN_TIMEOUT = "/usr/bin/timeout" UPDATE_CA_TRUST = "/usr/bin/update-ca-trust" BIN_WGET = "/usr/bin/wget" ZIP = "/usr/bin/zip" diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py index 4116d974e..6a06a8e95 100644 --- a/ipapython/ipautil.py +++ b/ipapython/ipautil.py @@ -249,7 +249,7 @@ def shell_quote(string): def run(args, stdin=None, raiseonerr=True, nolog=(), env=None, capture_output=True, skip_output=False, cwd=None, - runas=None): + runas=None, timeout=None): """ Execute a command and return stdin, stdout and the process return code. @@ -277,6 +277,8 @@ def run(args, stdin=None, raiseonerr=True, :param cwd: Current working directory :param runas: Name of a user that the command shold be run as. The spawned process will have both real and effective UID and GID set. + :param timeout: Timeout if the command hasn't returned within the specified + number of seconds. """ p_in = None p_out = None @@ -302,6 +304,11 @@ def run(args, stdin=None, raiseonerr=True, p_out = subprocess.PIPE p_err = subprocess.PIPE + if timeout: + # If a timeout was provided, use the timeout command + # to execute the requested command. + args[0:0] = [paths.BIN_TIMEOUT, str(timeout)] + arg_string = nolog_replace(' '.join(shell_quote(a) for a in args), nolog) root_logger.debug('Starting external process') root_logger.debug('args=%s' % arg_string) @@ -332,6 +339,9 @@ def run(args, stdin=None, raiseonerr=True, if skip_output: p_out.close() # pylint: disable=E1103 + if timeout and p.returncode == 124: + root_logger.debug('Process did not complete before timeout') + root_logger.debug('Process finished, return code=%s', p.returncode) # The command and its output may include passwords that we don't want -- cgit