|
Packit Service |
21c75c |
# misc.py
|
|
Packit Service |
21c75c |
# Copyright (C) 2012-2016 Red Hat, Inc.
|
|
Packit Service |
21c75c |
#
|
|
Packit Service |
21c75c |
# This copyrighted material is made available to anyone wishing to use,
|
|
Packit Service |
21c75c |
# modify, copy, or redistribute it subject to the terms and conditions of
|
|
Packit Service |
21c75c |
# the GNU General Public License v.2, or (at your option) any later version.
|
|
Packit Service |
21c75c |
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
Packit Service |
21c75c |
# ANY WARRANTY expressed or implied, including the implied warranties of
|
|
Packit Service |
21c75c |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
Packit Service |
21c75c |
# Public License for more details. You should have received a copy of the
|
|
Packit Service |
21c75c |
# GNU General Public License along with this program; if not, write to the
|
|
Packit Service |
21c75c |
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit Service |
21c75c |
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
|
|
Packit Service |
21c75c |
# source code or documentation are not subject to the GNU General Public
|
|
Packit Service |
21c75c |
# License and may only be used or replicated with the express permission of
|
|
Packit Service |
21c75c |
# Red Hat, Inc.
|
|
Packit Service |
21c75c |
#
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
Assorted utility functions for yum.
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
from __future__ import print_function, absolute_import
|
|
Packit Service |
21c75c |
from __future__ import unicode_literals
|
|
Packit Service |
21c75c |
from dnf.exceptions import MiscError
|
|
Packit Service |
21c75c |
from dnf.pycomp import base64_decodebytes, basestring, unicode
|
|
Packit Service |
21c75c |
from stat import *
|
|
Packit Service |
21c75c |
import libdnf.utils
|
|
Packit Service |
21c75c |
import dnf.const
|
|
Packit Service |
21c75c |
import dnf.crypto
|
|
Packit Service |
21c75c |
import dnf.exceptions
|
|
Packit Service |
21c75c |
import dnf.i18n
|
|
Packit Service |
21c75c |
import errno
|
|
Packit Service |
21c75c |
import glob
|
|
Packit Service |
21c75c |
import hashlib
|
|
Packit Service |
21c75c |
import io
|
|
Packit Service |
21c75c |
import os
|
|
Packit Service |
21c75c |
import os.path
|
|
Packit Service |
21c75c |
import pwd
|
|
Packit Service |
21c75c |
import re
|
|
Packit Service |
21c75c |
import shutil
|
|
Packit Service |
21c75c |
import tempfile
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
_available_checksums = set(['md5', 'sha1', 'sha256', 'sha384', 'sha512'])
|
|
Packit Service |
21c75c |
_default_checksums = ['sha256']
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
_re_compiled_glob_match = None
|
|
Packit Service |
21c75c |
def re_glob(s):
|
|
Packit Service |
21c75c |
""" Tests if a string is a shell wildcard. """
|
|
Packit Service |
21c75c |
global _re_compiled_glob_match
|
|
Packit Service |
21c75c |
if _re_compiled_glob_match is None:
|
|
Packit Service |
21c75c |
_re_compiled_glob_match = re.compile(r'[*?]|\[.+\]').search
|
|
Packit Service |
21c75c |
return _re_compiled_glob_match(s)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
_re_compiled_full_match = None
|
|
Packit Service |
21c75c |
def re_full_search_needed(s):
|
|
Packit Service |
21c75c |
""" Tests if a string needs a full nevra match, instead of just name. """
|
|
Packit Service |
21c75c |
global _re_compiled_full_match
|
|
Packit Service |
21c75c |
if _re_compiled_full_match is None:
|
|
Packit Service |
21c75c |
# A glob, or a "." or "-" separator, followed by something (the ".")
|
|
Packit Service |
21c75c |
one = re.compile(r'.*([-.*?]|\[.+\]).').match
|
|
Packit Service |
21c75c |
# Any epoch, for envra
|
|
Packit Service |
21c75c |
two = re.compile('[0-9]+:').match
|
|
Packit Service |
21c75c |
_re_compiled_full_match = (one, two)
|
|
Packit Service |
21c75c |
for rec in _re_compiled_full_match:
|
|
Packit Service |
21c75c |
if rec(s):
|
|
Packit Service |
21c75c |
return True
|
|
Packit Service |
21c75c |
return False
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
class Checksums(object):
|
|
Packit Service |
21c75c |
""" Generate checksum(s), on given pieces of data. Producing the
|
|
Packit Service |
21c75c |
Length and the result(s) when complete. """
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def __init__(self, checksums=None, ignore_missing=False, ignore_none=False):
|
|
Packit Service |
21c75c |
if checksums is None:
|
|
Packit Service |
21c75c |
checksums = _default_checksums
|
|
Packit Service |
21c75c |
self._sumalgos = []
|
|
Packit Service |
21c75c |
self._sumtypes = []
|
|
Packit Service |
21c75c |
self._len = 0
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
done = set()
|
|
Packit Service |
21c75c |
for sumtype in checksums:
|
|
Packit Service |
21c75c |
if sumtype == 'sha':
|
|
Packit Service |
21c75c |
sumtype = 'sha1'
|
|
Packit Service |
21c75c |
if sumtype in done:
|
|
Packit Service |
21c75c |
continue
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if sumtype in _available_checksums:
|
|
Packit Service |
21c75c |
sumalgo = hashlib.new(sumtype)
|
|
Packit Service |
21c75c |
elif ignore_missing:
|
|
Packit Service |
21c75c |
continue
|
|
Packit Service |
21c75c |
else:
|
|
Packit Service |
21c75c |
raise MiscError('Error Checksumming, bad checksum type %s' %
|
|
Packit Service |
21c75c |
sumtype)
|
|
Packit Service |
21c75c |
done.add(sumtype)
|
|
Packit Service |
21c75c |
self._sumtypes.append(sumtype)
|
|
Packit Service |
21c75c |
self._sumalgos.append(sumalgo)
|
|
Packit Service |
21c75c |
if not done and not ignore_none:
|
|
Packit Service |
21c75c |
raise MiscError('Error Checksumming, no valid checksum type')
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def __len__(self):
|
|
Packit Service |
21c75c |
return self._len
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
# Note that len(x) is assert limited to INT_MAX, which is 2GB on i686.
|
|
Packit Service |
21c75c |
length = property(fget=lambda self: self._len)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def update(self, data):
|
|
Packit Service |
21c75c |
self._len += len(data)
|
|
Packit Service |
21c75c |
for sumalgo in self._sumalgos:
|
|
Packit Service |
21c75c |
data = data.encode('utf-8') if isinstance(data, unicode) else data
|
|
Packit Service |
21c75c |
sumalgo.update(data)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def read(self, fo, size=2**16):
|
|
Packit Service |
21c75c |
data = fo.read(size)
|
|
Packit Service |
21c75c |
self.update(data)
|
|
Packit Service |
21c75c |
return data
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def hexdigests(self):
|
|
Packit Service |
21c75c |
ret = {}
|
|
Packit Service |
21c75c |
for sumtype, sumdata in zip(self._sumtypes, self._sumalgos):
|
|
Packit Service |
21c75c |
ret[sumtype] = sumdata.hexdigest()
|
|
Packit Service |
21c75c |
return ret
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def hexdigest(self, checksum=None):
|
|
Packit Service |
21c75c |
if checksum is None:
|
|
Packit Service |
21c75c |
if not self._sumtypes:
|
|
Packit Service |
21c75c |
return None
|
|
Packit Service |
21c75c |
checksum = self._sumtypes[0]
|
|
Packit Service |
21c75c |
if checksum == 'sha':
|
|
Packit Service |
21c75c |
checksum = 'sha1'
|
|
Packit Service |
21c75c |
return self.hexdigests()[checksum]
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def digests(self):
|
|
Packit Service |
21c75c |
ret = {}
|
|
Packit Service |
21c75c |
for sumtype, sumdata in zip(self._sumtypes, self._sumalgos):
|
|
Packit Service |
21c75c |
ret[sumtype] = sumdata.digest()
|
|
Packit Service |
21c75c |
return ret
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def digest(self, checksum=None):
|
|
Packit Service |
21c75c |
if checksum is None:
|
|
Packit Service |
21c75c |
if not self._sumtypes:
|
|
Packit Service |
21c75c |
return None
|
|
Packit Service |
21c75c |
checksum = self._sumtypes[0]
|
|
Packit Service |
21c75c |
if checksum == 'sha':
|
|
Packit Service |
21c75c |
checksum = 'sha1'
|
|
Packit Service |
21c75c |
return self.digests()[checksum]
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def get_default_chksum_type():
|
|
Packit Service |
21c75c |
return _default_checksums[0]
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def checksum(sumtype, file, CHUNK=2**16, datasize=None):
|
|
Packit Service |
21c75c |
"""takes filename, hand back Checksum of it
|
|
Packit Service |
21c75c |
sumtype = md5 or sha/sha1/sha256/sha512 (note sha == sha1)
|
|
Packit Service |
21c75c |
filename = /path/to/file
|
|
Packit Service |
21c75c |
CHUNK=65536 by default"""
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
# chunking brazenly lifted from Ryan Tomayko
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if isinstance(file, basestring):
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
with open(file, 'rb', CHUNK) as fo:
|
|
Packit Service |
21c75c |
return checksum(sumtype, fo, CHUNK, datasize)
|
|
Packit Service |
21c75c |
except (IOError, OSError):
|
|
Packit Service |
21c75c |
raise MiscError('Error opening file for checksum: %s' % file)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
# assumes file is a file-like-object
|
|
Packit Service |
21c75c |
data = Checksums([sumtype])
|
|
Packit Service |
21c75c |
while data.read(file, CHUNK):
|
|
Packit Service |
21c75c |
if datasize is not None and data.length > datasize:
|
|
Packit Service |
21c75c |
break
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
# This screws up the length, but that shouldn't matter. We only care
|
|
Packit Service |
21c75c |
# if this checksum == what we expect.
|
|
Packit Service |
21c75c |
if datasize is not None and datasize != data.length:
|
|
Packit Service |
21c75c |
return '!%u!%s' % (datasize, data.hexdigest(sumtype))
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
return data.hexdigest(sumtype)
|
|
Packit Service |
21c75c |
except (IOError, OSError) as e:
|
|
Packit Service |
21c75c |
raise MiscError('Error reading file for checksum: %s' % file)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
class GenericHolder(object):
|
|
Packit Service |
21c75c |
"""Generic Holder class used to hold other objects of known types
|
|
Packit Service |
21c75c |
It exists purely to be able to do object.somestuff, object.someotherstuff
|
|
Packit Service |
21c75c |
or object[key] and pass object to another function that will
|
|
Packit Service |
21c75c |
understand it"""
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def __init__(self, iter=None):
|
|
Packit Service |
21c75c |
self.__iter = iter
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def __iter__(self):
|
|
Packit Service |
21c75c |
if self.__iter is not None:
|
|
Packit Service |
21c75c |
return iter(self[self.__iter])
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def __getitem__(self, item):
|
|
Packit Service |
21c75c |
if hasattr(self, item):
|
|
Packit Service |
21c75c |
return getattr(self, item)
|
|
Packit Service |
21c75c |
else:
|
|
Packit Service |
21c75c |
raise KeyError(item)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def all_lists(self):
|
|
Packit Service |
21c75c |
"""Return a dictionary of all lists."""
|
|
Packit Service |
21c75c |
return {key: list_ for key, list_ in vars(self).items()
|
|
Packit Service |
21c75c |
if type(list_) is list}
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def merge_lists(self, other):
|
|
Packit Service |
21c75c |
""" Concatenate the list attributes from 'other' to ours. """
|
|
Packit Service |
21c75c |
for (key, val) in other.all_lists().items():
|
|
Packit Service |
21c75c |
vars(self).setdefault(key, []).extend(val)
|
|
Packit Service |
21c75c |
return self
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def procgpgkey(rawkey):
|
|
Packit Service |
21c75c |
'''Convert ASCII-armored GPG key to binary
|
|
Packit Service |
21c75c |
'''
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
# Normalise newlines
|
|
Packit Service |
21c75c |
rawkey = re.sub(b'\r\n?', b'\n', rawkey)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
# Extract block
|
|
Packit Service |
21c75c |
block = io.BytesIO()
|
|
Packit Service |
21c75c |
inblock = 0
|
|
Packit Service |
21c75c |
pastheaders = 0
|
|
Packit Service |
21c75c |
for line in rawkey.split(b'\n'):
|
|
Packit Service |
21c75c |
if line.startswith(b'-----BEGIN PGP PUBLIC KEY BLOCK-----'):
|
|
Packit Service |
21c75c |
inblock = 1
|
|
Packit Service |
21c75c |
elif inblock and line.strip() == b'':
|
|
Packit Service |
21c75c |
pastheaders = 1
|
|
Packit Service |
21c75c |
elif inblock and line.startswith(b'-----END PGP PUBLIC KEY BLOCK-----'):
|
|
Packit Service |
21c75c |
# Hit the end of the block, get out
|
|
Packit Service |
21c75c |
break
|
|
Packit Service |
21c75c |
elif pastheaders and line.startswith(b'='):
|
|
Packit Service |
21c75c |
# Hit the CRC line, don't include this and stop
|
|
Packit Service |
21c75c |
break
|
|
Packit Service |
21c75c |
elif pastheaders:
|
|
Packit Service |
21c75c |
block.write(line + b'\n')
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
# Decode and return
|
|
Packit Service |
21c75c |
return base64_decodebytes(block.getvalue())
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def keyInstalled(ts, keyid, timestamp):
|
|
Packit Service |
21c75c |
'''
|
|
Packit Service |
21c75c |
Return if the GPG key described by the given keyid and timestamp are
|
|
Packit Service |
21c75c |
installed in the rpmdb.
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
The keyid and timestamp should both be passed as integers.
|
|
Packit Service |
21c75c |
The ts is an rpm transaction set object
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
Return values:
|
|
Packit Service |
21c75c |
- -1 key is not installed
|
|
Packit Service |
21c75c |
- 0 key with matching ID and timestamp is installed
|
|
Packit Service |
21c75c |
- 1 key with matching ID is installed but has an older timestamp
|
|
Packit Service |
21c75c |
- 2 key with matching ID is installed but has a newer timestamp
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
No effort is made to handle duplicates. The first matching keyid is used to
|
|
Packit Service |
21c75c |
calculate the return result.
|
|
Packit Service |
21c75c |
'''
|
|
Packit Service |
21c75c |
# Search
|
|
Packit Service |
21c75c |
for hdr in ts.dbMatch('name', 'gpg-pubkey'):
|
|
Packit Service |
21c75c |
if hdr['version'] == keyid:
|
|
Packit Service |
21c75c |
installedts = int(hdr['release'], 16)
|
|
Packit Service |
21c75c |
if installedts == timestamp:
|
|
Packit Service |
21c75c |
return 0
|
|
Packit Service |
21c75c |
elif installedts < timestamp:
|
|
Packit Service |
21c75c |
return 1
|
|
Packit Service |
21c75c |
else:
|
|
Packit Service |
21c75c |
return 2
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
return -1
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def import_key_to_pubring(rawkey, keyid, gpgdir=None, make_ro_copy=True):
|
|
Packit Service |
21c75c |
if not os.path.exists(gpgdir):
|
|
Packit Service |
21c75c |
os.makedirs(gpgdir)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
with dnf.crypto.pubring_dir(gpgdir), dnf.crypto.Context() as ctx:
|
|
Packit Service |
21c75c |
# import the key
|
|
Packit Service |
21c75c |
with open(os.path.join(gpgdir, 'gpg.conf'), 'wb') as fp:
|
|
Packit Service |
21c75c |
fp.write(b'')
|
|
Packit Service |
21c75c |
ctx.op_import(rawkey)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
if make_ro_copy:
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
rodir = gpgdir + '-ro'
|
|
Packit Service |
21c75c |
if not os.path.exists(rodir):
|
|
Packit Service |
21c75c |
os.makedirs(rodir, mode=0o755)
|
|
Packit Service |
21c75c |
for f in glob.glob(gpgdir + '/*'):
|
|
Packit Service |
21c75c |
basename = os.path.basename(f)
|
|
Packit Service |
21c75c |
ro_f = rodir + '/' + basename
|
|
Packit Service |
21c75c |
shutil.copy(f, ro_f)
|
|
Packit Service |
21c75c |
os.chmod(ro_f, 0o755)
|
|
Packit Service |
21c75c |
# yes it is this stupid, why do you ask?
|
|
Packit Service |
21c75c |
opts = """lock-never
|
|
Packit Service |
21c75c |
no-auto-check-trustdb
|
|
Packit Service |
21c75c |
trust-model direct
|
|
Packit Service |
21c75c |
no-expensive-trust-checks
|
|
Packit Service |
21c75c |
no-permission-warning
|
|
Packit Service |
21c75c |
preserve-permissions
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
with open(os.path.join(rodir, 'gpg.conf'), 'w', 0o755) as fp:
|
|
Packit Service |
21c75c |
fp.write(opts)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
return True
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def getCacheDir():
|
|
Packit Service |
21c75c |
"""return a path to a valid and safe cachedir - only used when not running
|
|
Packit Service |
21c75c |
as root or when --tempcache is set"""
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
uid = os.geteuid()
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
usertup = pwd.getpwuid(uid)
|
|
Packit Service |
21c75c |
username = dnf.i18n.ucd(usertup[0])
|
|
Packit Service |
21c75c |
prefix = '%s-%s-' % (dnf.const.PREFIX, username)
|
|
Packit Service |
21c75c |
except KeyError:
|
|
Packit Service |
21c75c |
prefix = '%s-%s-' % (dnf.const.PREFIX, uid)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
# check for /var/tmp/prefix-* -
|
|
Packit Service |
21c75c |
dirpath = '%s/%s*' % (dnf.const.TMPDIR, prefix)
|
|
Packit Service |
21c75c |
cachedirs = sorted(glob.glob(dirpath))
|
|
Packit Service |
21c75c |
for thisdir in cachedirs:
|
|
Packit Service |
21c75c |
stats = os.lstat(thisdir)
|
|
Packit Service |
21c75c |
if S_ISDIR(stats[0]) and S_IMODE(stats[0]) == 448 and stats[4] == uid:
|
|
Packit Service |
21c75c |
return thisdir
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
# make the dir (tempfile.mkdtemp())
|
|
Packit Service |
21c75c |
cachedir = tempfile.mkdtemp(prefix=prefix, dir=dnf.const.TMPDIR)
|
|
Packit Service |
21c75c |
return cachedir
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def seq_max_split(seq, max_entries):
|
|
Packit Service |
21c75c |
""" Given a seq, split into a list of lists of length max_entries each. """
|
|
Packit Service |
21c75c |
ret = []
|
|
Packit Service |
21c75c |
num = len(seq)
|
|
Packit Service |
21c75c |
seq = list(seq) # Trying to use a set/etc. here is bad
|
|
Packit Service |
21c75c |
beg = 0
|
|
Packit Service |
21c75c |
while num > max_entries:
|
|
Packit Service |
21c75c |
end = beg + max_entries
|
|
Packit Service |
21c75c |
ret.append(seq[beg:end])
|
|
Packit Service |
21c75c |
beg += max_entries
|
|
Packit Service |
21c75c |
num -= max_entries
|
|
Packit Service |
21c75c |
ret.append(seq[beg:])
|
|
Packit Service |
21c75c |
return ret
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def unlink_f(filename):
|
|
Packit Service |
21c75c |
""" Call os.unlink, but don't die if the file isn't there. This is the main
|
|
Packit Service |
21c75c |
difference between "rm -f" and plain "rm". """
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
os.unlink(filename)
|
|
Packit Service |
21c75c |
except OSError as e:
|
|
Packit Service |
21c75c |
if e.errno != errno.ENOENT:
|
|
Packit Service |
21c75c |
raise
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def stat_f(filename, ignore_EACCES=False):
|
|
Packit Service |
21c75c |
""" Call os.stat(), but don't die if the file isn't there. Returns None. """
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
return os.stat(filename)
|
|
Packit Service |
21c75c |
except OSError as e:
|
|
Packit Service |
21c75c |
if e.errno in (errno.ENOENT, errno.ENOTDIR):
|
|
Packit Service |
21c75c |
return None
|
|
Packit Service |
21c75c |
if ignore_EACCES and e.errno == errno.EACCES:
|
|
Packit Service |
21c75c |
return None
|
|
Packit Service |
21c75c |
raise
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def _getloginuid():
|
|
Packit Service |
21c75c |
""" Get the audit-uid/login-uid, if available. os.getuid() is returned
|
|
Packit Service |
21c75c |
instead if there was a problem. Note that no caching is done here. """
|
|
Packit Service |
21c75c |
# We might normally call audit.audit_getloginuid(), except that requires
|
|
Packit Service |
21c75c |
# importing all of the audit module. And it doesn't work anyway: BZ 518721
|
|
Packit Service |
21c75c |
try:
|
|
Packit Service |
21c75c |
with open("/proc/self/loginuid") as fo:
|
|
Packit Service |
21c75c |
data = fo.read()
|
|
Packit Service |
21c75c |
return int(data)
|
|
Packit Service |
21c75c |
except (IOError, ValueError):
|
|
Packit Service |
21c75c |
return os.getuid()
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
_cached_getloginuid = None
|
|
Packit Service |
21c75c |
def getloginuid():
|
|
Packit Service |
21c75c |
""" Get the audit-uid/login-uid, if available. os.getuid() is returned
|
|
Packit Service |
21c75c |
instead if there was a problem. The value is cached, so you don't
|
|
Packit Service |
21c75c |
have to save it. """
|
|
Packit Service |
21c75c |
global _cached_getloginuid
|
|
Packit Service |
21c75c |
if _cached_getloginuid is None:
|
|
Packit Service |
21c75c |
_cached_getloginuid = _getloginuid()
|
|
Packit Service |
21c75c |
return _cached_getloginuid
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
|
|
Packit Service |
8e7813 |
def decompress(filename, dest=None, check_timestamps=False):
|
|
Packit Service |
8e7813 |
"""take a filename and decompress it into the same relative location.
|
|
Packit Service |
8e7813 |
When the compression type is not recognized (or file is not compressed),
|
|
Packit Service |
8e7813 |
the content of the file is copied to the destination"""
|
|
Packit Service |
8e7813 |
|
|
Packit Service |
8e7813 |
if dest:
|
|
Packit Service |
8e7813 |
out = dest
|
|
Packit Service |
8e7813 |
else:
|
|
Packit Service |
8e7813 |
out = None
|
|
Packit Service |
8e7813 |
dot_pos = filename.rfind('.')
|
|
Packit Service |
8e7813 |
if dot_pos > 0:
|
|
Packit Service |
8e7813 |
ext = filename[dot_pos:]
|
|
Packit Service |
8e7813 |
if ext in ('.zck', '.xz', '.bz2', '.gz', '.lzma', '.zst'):
|
|
Packit Service |
8e7813 |
out = filename[:dot_pos]
|
|
Packit Service |
8e7813 |
if out is None:
|
|
Packit Service |
8e7813 |
raise dnf.exceptions.MiscError("Could not determine destination filename")
|
|
Packit Service |
8e7813 |
|
|
Packit Service |
8e7813 |
if check_timestamps:
|
|
Packit Service |
8e7813 |
fi = stat_f(filename)
|
|
Packit Service |
8e7813 |
fo = stat_f(out)
|
|
Packit Service |
8e7813 |
if fi and fo and fo.st_mtime == fi.st_mtime:
|
|
Packit Service |
8e7813 |
return out
|
|
Packit Service |
21c75c |
|
|
Packit Service |
8e7813 |
try:
|
|
Packit Service |
8e7813 |
# libdnf.utils.decompress either decompress file to the destination or
|
|
Packit Service |
8e7813 |
# copy the content if the compression type is not recognized
|
|
Packit Service |
8e7813 |
libdnf.utils.decompress(filename, out, 0o644)
|
|
Packit Service |
8e7813 |
except RuntimeError as e:
|
|
Packit Service |
8e7813 |
raise dnf.exceptions.MiscError(str(e))
|
|
Packit Service |
21c75c |
|
|
Packit Service |
8e7813 |
if check_timestamps and fi:
|
|
Packit Service |
8e7813 |
os.utime(out, (fi.st_mtime, fi.st_mtime))
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
return out
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def calculate_repo_gen_dest(filename, generated_name):
|
|
Packit Service |
21c75c |
dest = os.path.dirname(filename)
|
|
Packit Service |
21c75c |
dest += '/gen'
|
|
Packit Service |
21c75c |
if not os.path.exists(dest):
|
|
Packit Service |
21c75c |
os.makedirs(dest, mode=0o755)
|
|
Packit Service |
21c75c |
return dest + '/' + generated_name
|
|
Packit Service |
21c75c |
|
|
Packit Service |
8e7813 |
|
|
Packit Service |
8e7813 |
def repo_gen_decompress(filename, generated_name):
|
|
Packit Service |
21c75c |
""" This is a wrapper around decompress, where we work out a cached
|
|
Packit Service |
21c75c |
generated name, and use check_timestamps. filename _must_ be from
|
|
Packit Service |
21c75c |
a repo. and generated_name is the type of the file. """
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
dest = calculate_repo_gen_dest(filename, generated_name)
|
|
Packit Service |
8e7813 |
return decompress(filename, dest=dest, check_timestamps=True)
|
|
Packit Service |
21c75c |
|
|
Packit Service |
21c75c |
def read_in_items_from_dot_dir(thisglob, line_as_list=True):
|
|
Packit Service |
21c75c |
""" Takes a glob of a dir (like /etc/foo.d/\\*.foo) returns a list of all
|
|
Packit Service |
21c75c |
the lines in all the files matching that glob, ignores comments and blank
|
|
Packit Service |
21c75c |
lines, optional paramater 'line_as_list tells whether to treat each line
|
|
Packit Service |
21c75c |
as a space or comma-separated list, defaults to True.
|
|
Packit Service |
21c75c |
"""
|
|
Packit Service |
21c75c |
results = []
|
|
Packit Service |
21c75c |
for fname in glob.glob(thisglob):
|
|
Packit Service |
21c75c |
with open(fname) as f:
|
|
Packit Service |
21c75c |
for line in f:
|
|
Packit Service |
21c75c |
if re.match(r'\s*(#|$)', line):
|
|
Packit Service |
21c75c |
continue
|
|
Packit Service |
21c75c |
line = line.rstrip() # no more trailing \n's
|
|
Packit Service |
21c75c |
line = line.lstrip() # be nice
|
|
Packit Service |
21c75c |
if not line:
|
|
Packit Service |
21c75c |
continue
|
|
Packit Service |
21c75c |
if line_as_list:
|
|
Packit Service |
21c75c |
line = line.replace('\n', ' ')
|
|
Packit Service |
21c75c |
line = line.replace(',', ' ')
|
|
Packit Service |
21c75c |
results.extend(line.split())
|
|
Packit Service |
21c75c |
continue
|
|
Packit Service |
21c75c |
results.append(line)
|
|
Packit Service |
21c75c |
return results
|