Imported Debian patch 4.0.5-6~numeezy
This commit is contained in:
committed by
Mario Fetka
parent
c44de33144
commit
10dfc9587b
@@ -19,6 +19,7 @@
|
||||
|
||||
import os
|
||||
import stat
|
||||
import re
|
||||
import sys
|
||||
import tempfile
|
||||
import shutil
|
||||
@@ -26,30 +27,44 @@ import xml.dom.minidom
|
||||
import pwd
|
||||
import base64
|
||||
from hashlib import sha1
|
||||
import fcntl
|
||||
import time
|
||||
import datetime
|
||||
|
||||
from six.moves import configparser
|
||||
from nss import nss
|
||||
from nss.error import NSPRError
|
||||
|
||||
from ipapython.ipa_log_manager import root_logger
|
||||
from ipapython import dogtag
|
||||
from ipapython import sysrestore
|
||||
from ipapython import ipautil
|
||||
from ipapython import certmonger
|
||||
from ipapython.certdb import get_ca_nickname, find_cert_from_txt, NSSDatabase
|
||||
from ipapython.certdb import get_ca_nickname
|
||||
from ipapython.dn import DN
|
||||
from ipalib import pkcs10, x509, api
|
||||
from ipalib.errors import CertificateOperationError
|
||||
from ipalib.text import _
|
||||
from ipaplatform import services
|
||||
from ipaplatform.constants import constants
|
||||
from ipaplatform.paths import paths
|
||||
|
||||
# Apache needs access to this database so we need to create it
|
||||
# where apache can reach
|
||||
NSS_DIR = paths.HTTPD_ALIAS_DIR
|
||||
|
||||
def find_cert_from_txt(cert, start=0):
|
||||
"""
|
||||
Given a cert blob (str) which may or may not contian leading and
|
||||
trailing text, pull out just the certificate part. This will return
|
||||
the FIRST cert in a stream of data.
|
||||
|
||||
Returns a tuple (certificate, last position in cert)
|
||||
"""
|
||||
s = cert.find('-----BEGIN CERTIFICATE-----', start)
|
||||
e = cert.find('-----END CERTIFICATE-----', s)
|
||||
if e > 0: e = e + 25
|
||||
|
||||
if s < 0 or e < 0:
|
||||
raise RuntimeError("Unable to find certificate")
|
||||
|
||||
cert = cert[s:e]
|
||||
return (cert, e)
|
||||
|
||||
def get_cert_nickname(cert):
|
||||
"""
|
||||
@@ -68,6 +83,213 @@ def get_cert_nickname(cert):
|
||||
return (str(dn[0]), dn)
|
||||
|
||||
|
||||
class NSSDatabase(object):
|
||||
"""A general-purpose wrapper around a NSS cert database
|
||||
|
||||
For permanent NSS databases, pass the cert DB directory to __init__
|
||||
|
||||
For temporary databases, do not pass nssdir, and call close() when done
|
||||
to remove the DB. Alternatively, a NSSDatabase can be used as a
|
||||
context manager that calls close() automatically.
|
||||
"""
|
||||
# Traditionally, we used CertDB for our NSS DB operations, but that class
|
||||
# got too tied to IPA server details, killing reusability.
|
||||
# BaseCertDB is a class that knows nothing about IPA.
|
||||
# Generic NSS DB code should be moved here.
|
||||
def __init__(self, nssdir=None):
|
||||
if nssdir is None:
|
||||
self.secdir = tempfile.mkdtemp()
|
||||
self._is_temporary = True
|
||||
else:
|
||||
self.secdir = nssdir
|
||||
self._is_temporary = False
|
||||
|
||||
def close(self):
|
||||
if self._is_temporary:
|
||||
shutil.rmtree(self.secdir)
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, tb):
|
||||
self.close()
|
||||
|
||||
def run_certutil(self, args, stdin=None):
|
||||
new_args = [paths.CERTUTIL, "-d", self.secdir]
|
||||
new_args = new_args + args
|
||||
return ipautil.run(new_args, stdin)
|
||||
|
||||
def create_db(self, password_filename):
|
||||
"""Create cert DB
|
||||
|
||||
:param password_filename: Name of file containing the database password
|
||||
"""
|
||||
self.run_certutil(["-N", "-f", password_filename])
|
||||
|
||||
def list_certs(self):
|
||||
"""Return nicknames and cert flags for all certs in the database
|
||||
|
||||
:return: List of (name, trust_flags) tuples
|
||||
"""
|
||||
certs, stderr, returncode = self.run_certutil(["-L"])
|
||||
certs = certs.splitlines()
|
||||
|
||||
# FIXME, this relies on NSS never changing the formatting of certutil
|
||||
certlist = []
|
||||
for cert in certs:
|
||||
nickname = cert[0:61]
|
||||
trust = cert[61:]
|
||||
if re.match(r'\w*,\w*,\w*', trust):
|
||||
certlist.append((nickname.strip(), trust.strip()))
|
||||
|
||||
return tuple(certlist)
|
||||
|
||||
def find_server_certs(self):
|
||||
"""Return nicknames and cert flags for server certs in the database
|
||||
|
||||
Server certs have an "u" character in the trust flags.
|
||||
|
||||
:return: List of (name, trust_flags) tuples
|
||||
"""
|
||||
server_certs = []
|
||||
for name, flags in self.list_certs():
|
||||
if 'u' in flags:
|
||||
server_certs.append((name, flags))
|
||||
|
||||
return server_certs
|
||||
|
||||
def get_trust_chain(self, nickname):
|
||||
"""Return names of certs in a given cert's trust chain
|
||||
|
||||
:param nickname: Name of the cert
|
||||
:return: List of certificate names
|
||||
"""
|
||||
root_nicknames = []
|
||||
chain, stderr, returncode = self.run_certutil([
|
||||
"-O", "-n", nickname])
|
||||
chain = chain.splitlines()
|
||||
|
||||
for c in chain:
|
||||
m = re.match('\s*"(.*)" \[.*', c)
|
||||
if m:
|
||||
root_nicknames.append(m.groups()[0])
|
||||
|
||||
return root_nicknames
|
||||
|
||||
def import_pkcs12(self, pkcs12_filename, db_password_filename,
|
||||
pkcs12_passwd=None):
|
||||
args = [paths.PK12UTIL, "-d", self.secdir,
|
||||
"-i", pkcs12_filename,
|
||||
"-k", db_password_filename, '-v']
|
||||
if pkcs12_passwd is not None:
|
||||
pkcs12_passwd = pkcs12_passwd + '\n'
|
||||
args = args + ["-w", paths.DEV_STDIN]
|
||||
try:
|
||||
ipautil.run(args, stdin=pkcs12_passwd)
|
||||
except ipautil.CalledProcessError, e:
|
||||
if e.returncode == 17:
|
||||
raise RuntimeError("incorrect password for pkcs#12 file %s" %
|
||||
pkcs12_filename)
|
||||
elif e.returncode == 10:
|
||||
raise RuntimeError("Failed to open %s" % pkcs12_filename)
|
||||
else:
|
||||
raise RuntimeError("unknown error import pkcs#12 file %s" %
|
||||
pkcs12_filename)
|
||||
|
||||
def trust_root_cert(self, root_nickname):
|
||||
if root_nickname[:7] == "Builtin":
|
||||
root_logger.debug(
|
||||
"No need to add trust for built-in root CAs, skipping %s" %
|
||||
root_nickname)
|
||||
else:
|
||||
try:
|
||||
self.run_certutil(["-M", "-n", root_nickname,
|
||||
"-t", "CT,CT,"])
|
||||
except ipautil.CalledProcessError, e:
|
||||
raise RuntimeError(
|
||||
"Setting trust on %s failed" % root_nickname)
|
||||
|
||||
def get_cert(self, nickname, pem=False):
|
||||
args = ['-L', '-n', nickname]
|
||||
if pem:
|
||||
args.append('-a')
|
||||
else:
|
||||
args.append('-r')
|
||||
try:
|
||||
cert, err, returncode = self.run_certutil(args)
|
||||
except ipautil.CalledProcessError:
|
||||
raise RuntimeError("Failed to get %s" % nickname)
|
||||
return cert
|
||||
|
||||
def export_pem_cert(self, nickname, location):
|
||||
"""Export the given cert to PEM file in the given location"""
|
||||
cert = self.get_cert(nickname)
|
||||
with open(location, "w+") as fd:
|
||||
fd.write(cert)
|
||||
os.chmod(location, 0444)
|
||||
|
||||
def import_pem_cert(self, nickname, flags, location):
|
||||
"""Import a cert form the given PEM file.
|
||||
|
||||
The file must contain exactly one certificate.
|
||||
"""
|
||||
try:
|
||||
with open(location) as fd:
|
||||
certs = fd.read()
|
||||
except IOError as e:
|
||||
raise RuntimeError(
|
||||
"Failed to open %s: %s" % (location, e.strerror)
|
||||
)
|
||||
|
||||
cert, st = find_cert_from_txt(certs)
|
||||
self.add_single_pem_cert(nickname, flags, cert)
|
||||
|
||||
try:
|
||||
find_cert_from_txt(certs, st)
|
||||
except RuntimeError:
|
||||
pass
|
||||
else:
|
||||
raise ValueError('%s contains more than one certificate' %
|
||||
location)
|
||||
|
||||
def add_single_pem_cert(self, nick, flags, cert):
|
||||
"""Import a cert in PEM format"""
|
||||
self.run_certutil(["-A", "-n", nick,
|
||||
"-t", flags,
|
||||
"-a"],
|
||||
stdin=cert)
|
||||
|
||||
def delete_cert(self, nick):
|
||||
self.run_certutil(["-D", "-n", nick])
|
||||
|
||||
def verify_server_cert_validity(self, nickname, hostname):
|
||||
"""Verify a certificate is valid for a SSL server with given hostname
|
||||
|
||||
Raises a ValueError if the certificate is invalid.
|
||||
"""
|
||||
certdb = cert = None
|
||||
nss.nss_init(self.secdir)
|
||||
try:
|
||||
certdb = nss.get_default_certdb()
|
||||
cert = nss.find_cert_from_nickname(nickname)
|
||||
intended_usage = nss.certificateUsageSSLServer
|
||||
try:
|
||||
approved_usage = cert.verify_now(certdb, True, intended_usage)
|
||||
except NSPRError, e:
|
||||
if e.errno != -8102:
|
||||
raise ValueError(e.strerror)
|
||||
approved_usage = 0
|
||||
if not approved_usage & intended_usage:
|
||||
raise ValueError('invalid for a SSL server')
|
||||
if not cert.verify_hostname(hostname):
|
||||
raise ValueError('invalid for server %s' % hostname)
|
||||
finally:
|
||||
del certdb, cert
|
||||
nss.nss_shutdown()
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class CertDB(object):
|
||||
"""An IPA-server-specific wrapper around NSS
|
||||
|
||||
@@ -97,7 +319,7 @@ class CertDB(object):
|
||||
self.subject_base = subject_base
|
||||
try:
|
||||
self.cwd = os.getcwd()
|
||||
except OSError as e:
|
||||
except OSError, e:
|
||||
raise RuntimeError("Unable to determine the current directory: %s" % str(e))
|
||||
|
||||
if not subject_base:
|
||||
@@ -164,8 +386,8 @@ class CertDB(object):
|
||||
def gen_password(self):
|
||||
return sha1(ipautil.ipa_generate_password()).hexdigest()
|
||||
|
||||
def run_certutil(self, args, stdin=None, **kwargs):
|
||||
return self.nssdb.run_certutil(args, stdin, **kwargs)
|
||||
def run_certutil(self, args, stdin=None):
|
||||
return self.nssdb.run_certutil(args, stdin)
|
||||
|
||||
def run_signtool(self, args, stdin=None):
|
||||
with open(self.passwd_fname, "r") as f:
|
||||
@@ -228,12 +450,11 @@ class CertDB(object):
|
||||
do that step."""
|
||||
# export the CA cert for use with other apps
|
||||
ipautil.backup_file(self.cacert_fname)
|
||||
root_nicknames = self.find_root_cert(nickname)[:-1]
|
||||
root_nicknames = self.find_root_cert(nickname)
|
||||
fd = open(self.cacert_fname, "w")
|
||||
for root in root_nicknames:
|
||||
result = self.run_certutil(["-L", "-n", root, "-a"],
|
||||
capture_output=True)
|
||||
fd.write(result.output)
|
||||
(cert, stderr, returncode) = self.run_certutil(["-L", "-n", root, "-a"])
|
||||
fd.write(cert)
|
||||
fd.close()
|
||||
os.chmod(self.cacert_fname, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
|
||||
if create_pkcs12:
|
||||
@@ -245,7 +466,7 @@ class CertDB(object):
|
||||
"-k", self.passwd_fname])
|
||||
self.set_perms(self.pk12_fname)
|
||||
|
||||
def load_cacert(self, cacert_fname, trust_flags):
|
||||
def load_cacert(self, cacert_fname):
|
||||
"""
|
||||
Load all the certificates from a given file. It is assumed that
|
||||
this file creates CA certificates.
|
||||
@@ -264,7 +485,7 @@ class CertDB(object):
|
||||
nick = get_ca_nickname(self.realm)
|
||||
else:
|
||||
nick = str(subject_dn)
|
||||
self.nssdb.add_cert(cert, nick, trust_flags, pem=True)
|
||||
self.nssdb.add_single_pem_cert(nick, "CT,,C", cert)
|
||||
except RuntimeError:
|
||||
break
|
||||
|
||||
@@ -277,8 +498,7 @@ class CertDB(object):
|
||||
"""
|
||||
try:
|
||||
args = ["-L", "-n", nickname, "-a"]
|
||||
result = self.run_certutil(args, capture_output=True)
|
||||
cert = result.output
|
||||
(cert, err, returncode) = self.run_certutil(args)
|
||||
if pem:
|
||||
return cert
|
||||
else:
|
||||
@@ -297,10 +517,14 @@ class CertDB(object):
|
||||
/usr/lib[64]/ipa/certmonger.
|
||||
"""
|
||||
if command is not None and not os.path.isabs(command):
|
||||
command = paths.CERTMONGER_COMMAND_TEMPLATE % (command)
|
||||
if sys.maxsize > 2**32L:
|
||||
libpath = 'lib64'
|
||||
else:
|
||||
libpath = 'lib'
|
||||
command = paths.CERTMONGER_COMMAND_TEMPLATE % (libpath, command)
|
||||
try:
|
||||
request_id = certmonger.start_tracking(nickname, self.secdir, password_file, command)
|
||||
except RuntimeError as e:
|
||||
except RuntimeError, e:
|
||||
root_logger.error("certmonger failed starting to track certificate: %s" % str(e))
|
||||
return
|
||||
|
||||
@@ -316,7 +540,7 @@ class CertDB(object):
|
||||
"""
|
||||
try:
|
||||
certmonger.stop_tracking(self.secdir, nickname=nickname)
|
||||
except RuntimeError as e:
|
||||
except RuntimeError, e:
|
||||
root_logger.error("certmonger failed to stop tracking certificate: %s" % str(e))
|
||||
|
||||
def create_server_cert(self, nickname, hostname, other_certdb=None, subject=None):
|
||||
@@ -335,7 +559,7 @@ class CertDB(object):
|
||||
subject=DN(('CN', hostname), self.subject_base)
|
||||
self.request_cert(subject)
|
||||
cdb.issue_server_cert(self.certreq_fname, self.certder_fname)
|
||||
self.import_cert(self.certder_fname, nickname)
|
||||
self.add_cert(self.certder_fname, nickname)
|
||||
fd = open(self.certder_fname, "r")
|
||||
dercert = fd.read()
|
||||
fd.close()
|
||||
@@ -353,7 +577,7 @@ class CertDB(object):
|
||||
subject=DN(('CN', hostname), self.subject_base)
|
||||
self.request_cert(subject)
|
||||
cdb.issue_signing_cert(self.certreq_fname, self.certder_fname)
|
||||
self.import_cert(self.certder_fname, nickname)
|
||||
self.add_cert(self.certder_fname, nickname)
|
||||
os.unlink(self.certreq_fname)
|
||||
os.unlink(self.certder_fname)
|
||||
|
||||
@@ -368,10 +592,9 @@ class CertDB(object):
|
||||
"-z", self.noise_fname,
|
||||
"-f", self.passwd_fname,
|
||||
"-a"]
|
||||
result = self.run_certutil(args,
|
||||
capture_output=True, capture_error=True)
|
||||
(stdout, stderr, returncode) = self.run_certutil(args)
|
||||
os.remove(self.noise_fname)
|
||||
return (result.output, result.error_output)
|
||||
return (stdout, stderr)
|
||||
|
||||
def issue_server_cert(self, certreq_fname, cert_fname):
|
||||
self.setup_cert_request()
|
||||
@@ -387,7 +610,7 @@ class CertDB(object):
|
||||
# We just want the CSR bits, make sure there is nothing else
|
||||
csr = pkcs10.strip_header(csr)
|
||||
|
||||
params = {'profileId': dogtag.DEFAULT_PROFILE,
|
||||
params = {'profileId': 'caIPAserviceCert',
|
||||
'cert_request_type': 'pkcs10',
|
||||
'requestor_name': 'IPA Installer',
|
||||
'cert_request': csr,
|
||||
@@ -398,13 +621,17 @@ class CertDB(object):
|
||||
password = f.readline()
|
||||
f.close()
|
||||
result = dogtag.https_request(
|
||||
self.host_name, 8443, "/ca/ee/ca/profileSubmitSSLClient",
|
||||
self.host_name,
|
||||
api.env.ca_ee_install_port or
|
||||
dogtag.configured_constants().EE_SECURE_PORT,
|
||||
"/ca/ee/ca/profileSubmitSSLClient",
|
||||
self.secdir, password, "ipaCert", **params)
|
||||
http_status, http_headers, http_body = result
|
||||
http_status, http_reason_phrase, http_headers, http_body = result
|
||||
|
||||
if http_status != 200:
|
||||
raise CertificateOperationError(
|
||||
error=_('Unable to communicate with CMS (status %d)') % http_status)
|
||||
error=_('Unable to communicate with CMS (%s)') %
|
||||
http_reason_phrase)
|
||||
|
||||
# The result is an XML blob. Pull the certificate out of that
|
||||
doc = xml.dom.minidom.parseString(http_body)
|
||||
@@ -451,9 +678,12 @@ class CertDB(object):
|
||||
password = f.readline()
|
||||
f.close()
|
||||
result = dogtag.https_request(
|
||||
self.host_name, 8443, "/ca/ee/ca/profileSubmitSSLClient",
|
||||
self.host_name,
|
||||
api.env.ca_ee_install_port or
|
||||
dogtag.configured_constants().EE_SECURE_PORT,
|
||||
"/ca/ee/ca/profileSubmitSSLClient",
|
||||
self.secdir, password, "ipaCert", **params)
|
||||
http_status, http_headers, http_body = result
|
||||
http_status, http_reason_phrase, http_headers, http_body = result
|
||||
if http_status != 200:
|
||||
raise RuntimeError("Unable to submit cert request")
|
||||
|
||||
@@ -472,10 +702,7 @@ class CertDB(object):
|
||||
f.write(cert)
|
||||
f.close()
|
||||
|
||||
def add_cert(self, cert, nick, flags, pem=False):
|
||||
self.nssdb.add_cert(cert, nick, flags, pem)
|
||||
|
||||
def import_cert(self, cert_fname, nickname):
|
||||
def add_cert(self, cert_fname, nickname):
|
||||
"""
|
||||
Load a certificate from a PEM file and add minimal trust.
|
||||
"""
|
||||
@@ -512,7 +739,8 @@ class CertDB(object):
|
||||
f.write(pwdfile.read())
|
||||
f.close()
|
||||
pwdfile.close()
|
||||
self.set_perms(self.pwd_conf, uid=constants.HTTPD_USER)
|
||||
# TODO: replace explicit uid by a platform-specific one
|
||||
self.set_perms(self.pwd_conf, uid="apache")
|
||||
|
||||
def find_root_cert(self, nickname):
|
||||
"""
|
||||
@@ -523,13 +751,13 @@ class CertDB(object):
|
||||
|
||||
return root_nicknames
|
||||
|
||||
def trust_root_cert(self, root_nickname, trust_flags=None):
|
||||
def trust_root_cert(self, root_nickname):
|
||||
if root_nickname is None:
|
||||
root_logger.debug("Unable to identify root certificate to trust. Continuing but things are likely to fail.")
|
||||
return
|
||||
|
||||
try:
|
||||
self.nssdb.trust_root_cert(root_nickname, trust_flags)
|
||||
self.nssdb.trust_root_cert(root_nickname)
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
@@ -578,10 +806,10 @@ class CertDB(object):
|
||||
# a new certificate database.
|
||||
self.create_passwd_file(passwd)
|
||||
self.create_certdbs()
|
||||
self.load_cacert(cacert_fname, 'CT,C,C')
|
||||
self.load_cacert(cacert_fname)
|
||||
|
||||
def create_from_pkcs12(self, pkcs12_fname, pkcs12_passwd, passwd=None,
|
||||
ca_file=None, trust_flags=None):
|
||||
ca_file=None):
|
||||
"""Create a new NSS database using the certificates in a PKCS#12 file.
|
||||
|
||||
pkcs12_fname: the filename of the PKCS#12 file
|
||||
@@ -603,31 +831,19 @@ class CertDB(object):
|
||||
raise RuntimeError("Could not find a suitable server cert in import in %s" % pkcs12_fname)
|
||||
|
||||
if ca_file:
|
||||
try:
|
||||
with open(ca_file) as fd:
|
||||
certs = fd.read()
|
||||
except IOError as e:
|
||||
raise RuntimeError(
|
||||
"Failed to open %s: %s" % (ca_file, e.strerror))
|
||||
st = 0
|
||||
num = 1
|
||||
while True:
|
||||
try:
|
||||
cert, st = find_cert_from_txt(certs, st)
|
||||
except RuntimeError:
|
||||
break
|
||||
self.add_cert(cert, 'CA %s' % num, ',,', pem=True)
|
||||
num += 1
|
||||
self.nssdb.import_pem_cert('CA', 'CT,CT,', ca_file)
|
||||
|
||||
# We only handle one server cert
|
||||
nickname = server_certs[0][0]
|
||||
|
||||
ca_names = self.find_root_cert(nickname)[:-1]
|
||||
ca_names = [name for name, flags
|
||||
in self.nssdb.list_certs() if 'u' not in flags]
|
||||
if len(ca_names) == 0:
|
||||
raise RuntimeError("Could not find a CA cert in %s" % pkcs12_fname)
|
||||
|
||||
self.cacert_name = ca_names[-1]
|
||||
self.trust_root_cert(self.cacert_name, trust_flags)
|
||||
self.cacert_name = ca_names[0]
|
||||
for ca in ca_names:
|
||||
self.trust_root_cert(ca)
|
||||
|
||||
self.create_pin_file()
|
||||
self.export_ca_cert(nickname, False)
|
||||
@@ -640,119 +856,7 @@ class CertDB(object):
|
||||
|
||||
def publish_ca_cert(self, location):
|
||||
shutil.copy(self.cacert_fname, location)
|
||||
os.chmod(location, 0o444)
|
||||
os.chmod(location, 0444)
|
||||
|
||||
def export_pem_cert(self, nickname, location):
|
||||
return self.nssdb.export_pem_cert(nickname, location)
|
||||
|
||||
def request_service_cert(self, nickname, principal, host, pwdconf=False):
|
||||
self.create_from_cacert(paths.IPA_CA_CRT)
|
||||
if pwdconf:
|
||||
self.create_password_conf()
|
||||
reqid = certmonger.request_cert(nssdb=self.secdir,
|
||||
nickname=nickname,
|
||||
principal=principal,
|
||||
subject=host,
|
||||
passwd_fname=self.passwd_fname)
|
||||
# Now wait for the cert to appear. Check three times then abort
|
||||
certmonger.wait_for_request(reqid, timeout=15)
|
||||
|
||||
|
||||
class _CrossProcessLock(object):
|
||||
_DATETIME_FORMAT = '%Y%m%d%H%M%S%f'
|
||||
|
||||
def __init__(self, filename):
|
||||
self._filename = filename
|
||||
|
||||
def __enter__(self):
|
||||
self.acquire()
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
self.release()
|
||||
|
||||
def acquire(self, owner=None):
|
||||
self._do(self._acquire, owner)
|
||||
|
||||
def release(self, owner=None):
|
||||
self._do(self._release, owner)
|
||||
|
||||
def _acquire(self, owner):
|
||||
now = datetime.datetime.utcnow()
|
||||
|
||||
if self._locked and now >= self._expire:
|
||||
self._locked = False
|
||||
|
||||
if self._locked:
|
||||
return False
|
||||
|
||||
self._locked = True
|
||||
self._owner = owner
|
||||
self._expire = now + datetime.timedelta(hours=1)
|
||||
|
||||
return True
|
||||
|
||||
def _release(self, owner):
|
||||
if not self._locked or self._owner != owner:
|
||||
raise RuntimeError("lock not acquired by %s" % owner)
|
||||
|
||||
self._locked = False
|
||||
self._owner = None
|
||||
self._expire = None
|
||||
|
||||
return True
|
||||
|
||||
def _do(self, func, owner):
|
||||
if owner is None:
|
||||
owner = '%s[%s]' % (os.path.basename(sys.argv[0]), os.getpid())
|
||||
|
||||
while True:
|
||||
with open(self._filename, 'a+') as f:
|
||||
fcntl.flock(f, fcntl.LOCK_EX)
|
||||
|
||||
f.seek(0)
|
||||
self._read(f)
|
||||
|
||||
if func(owner):
|
||||
f.seek(0)
|
||||
f.truncate()
|
||||
self._write(f)
|
||||
return
|
||||
|
||||
time.sleep(10)
|
||||
|
||||
def _read(self, fileobj):
|
||||
p = configparser.RawConfigParser()
|
||||
p.readfp(fileobj)
|
||||
|
||||
try:
|
||||
self._locked = p.getboolean('lock', 'locked')
|
||||
|
||||
if self._locked:
|
||||
self._owner = p.get('lock', 'owner')
|
||||
|
||||
expire = p.get('lock', 'expire')
|
||||
try:
|
||||
self._expire = datetime.datetime.strptime(
|
||||
expire, self._DATETIME_FORMAT)
|
||||
except ValueError:
|
||||
raise configparser.Error
|
||||
except configparser.Error:
|
||||
self._locked = False
|
||||
self._owner = None
|
||||
self._expire = None
|
||||
|
||||
def _write(self, fileobj):
|
||||
p = configparser.RawConfigParser()
|
||||
p.add_section('lock')
|
||||
|
||||
locked = '1' if self._locked else '0'
|
||||
p.set('lock', 'locked', locked)
|
||||
|
||||
if self._locked:
|
||||
expire = self._expire.strftime(self._DATETIME_FORMAT)
|
||||
p.set('lock', 'owner', self._owner)
|
||||
p.set('lock', 'expire', expire)
|
||||
|
||||
p.write(fileobj)
|
||||
|
||||
renewal_lock = _CrossProcessLock(paths.IPA_RENEWAL_LOCK)
|
||||
|
||||
Reference in New Issue
Block a user