曾与蒿藜同雨露,한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.终随松柏到冰霜.かつては雑草やヨモギと共に雨や露を分かち合っていたが、今では松やヒノキと共に霜や雪に耐えている。曾与蒿藜同雨露,Once sharing rain and dew with weeds and wormwood, now enduring frost and snow with pines and cypresses.终随松柏到冰霜.曾与蒿藜同雨露한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.,终随松柏到冰霜.譖セ荳手珍阯懷酔髮ィ髴イ�檎サ磯囂譚セ譟丞芦蜀ー髴�曾与蒿藜同雨露,鏇句笌钂胯棞鍚岄洦闇诧紝缁堥殢鏉炬煆鍒板啺闇�终随松柏到冰霜.曾与蒿藜同雨露,한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.终随松柏到冰霜.曾与蒿藜同雨露,终随松柏到冰霜.
import os.path
import re
from yum.i18n import _, P_
from yum.constants import *
from yum.logginglevels import INFO_1
import rpmUtils.miscutils
import misc
import fnmatch
# newpackages is weird, in that we'll never display that because we filter to
# things relevant to installed pkgs...
_update_info_types_ = ("security", "bugfix", "enhancement",
"recommended", "newpackage")
def _rpm_tup_vercmp(tup1, tup2):
""" Compare two "std." tuples, (n, a, e, v, r). """
return rpmUtils.miscutils.compareEVR((tup1[2], tup1[3], tup1[4]),
(tup2[2], tup2[3], tup2[4]))
def _ysp_safe_refs(refs):
""" Sometimes refs == None, if so return the empty list here.
So we don't have to check everywhere. """
if not refs:
return []
return refs
def _match_sec_cmd(sec_cmds, pkgname, notice):
for i in sec_cmds:
if fnmatch.fnmatch(pkgname, i):
return i
if fnmatch.fnmatch(notice['update_id'], i):
return i
cvei = i
if not (i.startswith("CVE-") or i.startswith("*")):
cvei = 'CVE-' + i
for ref in _ysp_safe_refs(notice['references']):
if ref['id'] is None:
continue
if fnmatch.fnmatch(ref['id'], i):
return i
if fnmatch.fnmatch(ref['id'], cvei):
return i
return None
def _has_id(used_map, refs, ref_type, ref_ids):
''' Check if the given ID is a match. '''
for ref in _ysp_safe_refs(refs):
if ref['type'] != ref_type:
continue
if ref['id'] not in ref_ids:
continue
used_map[ref_type][ref['id']] = True
return ref
return None
def _ysp_should_filter_pkg(opts, pkgname, notice, used_map):
""" Do the package filtering for should_show and should_keep. """
rcmd = _match_sec_cmd(opts.sec_cmds, pkgname, notice)
if rcmd:
used_map['cmd'][rcmd] = True
return True
elif opts.advisory and notice['update_id'] in opts.advisory:
used_map['id'][notice['update_id']] = True
return True
elif (opts.severity and notice['type'] == 'security' and
notice['severity'] in opts.severity):
used_map['sev'][notice['severity']] = True
return True
elif opts.cve and _has_id(used_map, notice['references'], "cve", opts.cve):
return True
elif opts.bz and _has_id(used_map, notice['references'],"bugzilla",opts.bz):
return True
# FIXME: Add opts for enhancement/etc.? -- __update_info_types__
elif (opts.security and notice['type'] == 'security' and
(not opts.severity or 'severity' not in notice or
not notice['severity'])):
return True
elif opts.bugfixes and notice['type'] == 'bugfix':
return True
elif not (opts.advisory or opts.cve or opts.bz or
opts.security or opts.bugfixes or opts.sec_cmds or opts.severity):
return True # This is only possible from should_show_pkg
return False
def _ysp_has_info_md(rname, md):
if rname in _update_info_types_:
if md['type'] == rname:
return md
for ref in _ysp_safe_refs(md['references']):
if ref['type'] != rname:
continue
return md
def _no_options(opts):
return not (opts.security or opts.bugfixes or
opts.advisory or opts.bz or opts.cve or opts.severity)
def _updateinfofilter2opts(updateinfo_filters):
opts = misc.GenericHolder()
opts.sec_cmds = []
opts.advisory = updateinfo_filters.get('advs', [])
opts.bz = updateinfo_filters.get('bzs', [])
opts.cve = updateinfo_filters.get('cves', [])
opts.severity = updateinfo_filters.get('sevs', [])
opts.bugfixes = updateinfo_filters.get('bugfix', False)
opts.security = updateinfo_filters.get('security', False)
return opts
def _args2filters(args):
# Basically allow args to turn into security filters, for shell command etc.
T_map = {'advs' : 'advs',
'advisory' : 'advs',
'advisories' : 'advs',
'bzs' : 'bzs',
'bz' : 'bzs',
'cves' : 'cves',
'cve' : 'cves',
'security-severity' : 'sevs',
'security-severities' : 'sevs',
'severity' : 'sevs',
'severities' : 'sevs',
'sevs' : 'sevs',
'sev' : 'sevs',
'security' : 'security',
'sec' : 'security',
'bugfix' : 'bugfix',
'bugfixes' : 'bugfix',
'bugs' : 'bugfix',
}
filters = {'security' : False, 'bugfix' : False}
for arg0 in args:
arg0 = arg0.replace(" ", ',')
T = 'advs'
if '=' in arg0:
T, arg1 = arg0.split('=', 1)
elif arg0 not in T_map:
arg1 = arg0
else:
T = arg0
arg1 = 'true'
if T not in T_map:
continue # Error message?
T = T_map[T]
if T in ('security', 'bugfix'):
filters[T] = not filters[T]
else:
filters[T] = filters.get(T, []) + arg1.split(',')
return filters
def _ysp_gen_opts(filters, sec_cmds=None):
def strip_respin(id_):
# Example: RHSA-2016:1234-2 -> RHSA-2016:1234
pattern = r'^(RH[BES]A\-\d+\:\d+)(\-\d+)?$'
match = re.match(pattern, id_)
if match:
return match.group(1)
return id_
opts = _updateinfofilter2opts(filters)
if sec_cmds is not None:
opts.sec_cmds = sec_cmds
# If a RH advisory was specified with a respin suffix, strip it out, as we
# don't include these suffixes in the notice update_id attribute either (we
# use the version attribute for that). Note that there's no ambiguity in
# which notice version we should match then, as updateinfo.xml should only
# contain one per advisory ID (we give a warning when duplicate IDs are
# detected in it). The reason we are handling this is that we sometimes
# refer to advisories in this form (e.g. on rhn.redhat.com/errata/...) and
# the user may then use it with yum too, in which case we would yield no
# matches.
#
# However, we used to put these suffixes in update_id in the past, so let's
# also keep the original (unstripped) form around in opts, just in case we
# are dealing with such an old updateinfo.xml.
for attr in ['sec_cmds', 'advisory']:
oldlist = getattr(opts, attr)
stripped = map(strip_respin, oldlist)
newlist = list(set(oldlist) | set(stripped))
setattr(opts, attr, newlist)
return opts
def _ysp_gen_used_map(opts):
used_map = {'bugzilla' : {}, 'cve' : {}, 'id' : {}, 'cmd' : {}, 'sev' : {}}
if True:
return used_map
for i in opts.sec_cmds:
used_map['cmd'][i] = False
for i in opts.advisory:
used_map['id'][i] = False
for i in opts.bz:
used_map['bugzilla'][i] = False
for i in opts.cve:
used_map['cve'][i] = False
for i in opts.severity:
used_map['sev'][i] = False
return used_map
def _ysp_chk_used_map(used_map, msg):
for i in used_map['cmd']:
if not used_map['cmd'][i]:
msg('No update information found for \"%s\"' % i)
for i in used_map['id']:
if not used_map['id'][i]:
msg('Advisory \"%s\" not found applicable for this system' % i)
for i in used_map['bugzilla']:
if not used_map['bugzilla'][i]:
msg('BZ \"%s\" not found applicable for this system' % i)
for i in used_map['cve']:
if not used_map['cve'][i]:
msg('CVE \"%s\" not found applicable for this system' % i)
for i in used_map['sev']:
if not used_map['sev'][i]:
msg('Severity \"%s\" not found applicable for this system' % i)
def _get_name2pkgtup(base, pkgtups):
name2tup = {}
for pkgtup in pkgtups:
# Get the latest "old" pkgtups
if (pkgtup[0] in name2tup and
_rpm_tup_vercmp(name2tup[pkgtup[0]], pkgtup) > 0):
continue
name2tup[pkgtup[0]] = pkgtup
return name2tup
def _get_name2oldpkgtup(base):
""" Get the pkgtups for all installed pkgs. which have an update. """
oupdates = map(lambda x: x[1], base.up.getUpdatesTuples())
return _get_name2pkgtup(base, oupdates)
def _get_name2instpkgtup(base):
""" Get the pkgtups for all installed pkgs. """
return _get_name2pkgtup(base, base.rpmdb.simplePkgList())
def _get_name2allpkgtup(base):
""" Get the pkgtups for all installed pkgs. and munge that to be the
first possible pkgtup. """
ofirst = [(pt[0], pt[1], '0','0','0') for pt in base.rpmdb.simplePkgList()]
return _get_name2pkgtup(base, ofirst)
def _get_name2aallpkgtup(base):
""" Get the pkgtups for all available pkgs. and munge that to be the
first possible pkgtup. """
ofirst = [(pt[0], pt[1],'0','0','0') for pt in base.pkgSack.simplePkgList()]
return _get_name2pkgtup(base, ofirst)
# You might think we'd just call delPackage
# and indeed that works for list updates etc.
#
# __but__ that doesn't work for dependancies on real updates
#
# So to fix deps. we need to do it at the preresolve stage and take the
# "transaction package list" and then remove packages from that.
#
# __but__ that doesn't work for lists ... so we do it two ways
#
def _ysp_should_keep_pkg(opts, pkgtup, md_info, used_map):
""" Do we want to keep this package to satisfy the security limits. """
name = pkgtup[0]
for (pkgtup, notice) in md_info.get_applicable_notices(pkgtup):
if _ysp_should_filter_pkg(opts, name, notice, used_map):
return True
return False
def _repos_downloaded(repos):
dled = True
for repo in repos:
try:
data = repo.repoXML.getData('updateinfo');
except:
continue # No data is fine...
# Note that this doesn't check that it's decompressed...
path = repo.cachedir +'/'+ os.path.basename(data.location[1])
if not os.path.exists(path):
dled = False
break
return dled
def _check_running_kernel(yb, md_info, msg):
kern_pkgtup = misc.get_running_kernel_pkgtup(yb.ts)
if kern_pkgtup[0] is None:
return
found_sec = False
for (pkgtup, notice) in md_info.get_applicable_notices(kern_pkgtup):
if found_sec or notice['type'] != 'security':
continue
found_sec = True
ipkg = yb.rpmdb.searchPkgTuple(pkgtup)
if not ipkg:
continue # Not installed
ipkg = ipkg[0]
e = ''
if kern_pkgtup[2] != '0':
e = '%s:' % kern_pkgtup[2]
rpkg = '%s-%s%s-%s.%s' % (kern_pkgtup[0], e,
kern_pkgtup[3], kern_pkgtup[4],
kern_pkgtup[1])
msg(_('Security: %s is an installed security update') % ipkg)
msg(_('Security: %s is the currently running version') % rpkg)
break
def remove_txmbrs(base, filters=None):
'''
Remove packages from the transaction, using the updateinfo data.
'''
def ysp_del_pkg(tspkg):
""" Deletes a package within a transaction. """
base.verbose_logger.log(INFO_1,
_(" --> %s from %s removed (updateinfo)") %
(tspkg.po, tspkg.po.ui_from_repo))
tsinfo.remove(tspkg.pkgtup)
if filters is None:
filters = base.updateinfo_filters
opts = _ysp_gen_opts(filters)
if _no_options(opts):
return 0, 0, 0
md_info = base.upinfo
tot = 0
cnt = 0
used_map = _ysp_gen_used_map(opts)
tsinfo = base.tsInfo
tspkgs = tsinfo.getMembers()
# Ok, here we keep any pkgs that pass "ysp" tests, then we keep all
# related pkgs ... Ie. "installed" version marked for removal.
keep_pkgs = set()
count_states = set(TS_INSTALL_STATES + [TS_ERASE])
count_pkgs = set()
for tspkg in tspkgs:
if tspkg.output_state in count_states:
count_pkgs.add(tspkg.po)
name2tup = _get_name2oldpkgtup(base)
for tspkg in tspkgs:
if tspkg.output_state in count_states:
tot += 1
name = tspkg.po.name
if (name not in name2tup or
not _ysp_should_keep_pkg(opts, name2tup[name], md_info, used_map)):
continue
if tspkg.output_state in count_states:
cnt += 1
keep_pkgs.add(tspkg.po)
scnt = cnt
mini_depsolve_again = True
while mini_depsolve_again:
mini_depsolve_again = False
for tspkg in tspkgs:
if tspkg.po in keep_pkgs:
# Find any related pkgs, and add them:
for (rpkg, reason) in tspkg.relatedto:
if rpkg not in keep_pkgs:
if rpkg in count_pkgs:
cnt += 1
keep_pkgs.add(rpkg)
mini_depsolve_again = True
else:
# If related to any keep pkgs, add us
for (rpkg, reason) in tspkg.relatedto:
if rpkg in keep_pkgs:
if rpkg in count_pkgs:
cnt += 1
keep_pkgs.add(tspkg.po)
mini_depsolve_again = True
break
for tspkg in tspkgs:
if tspkg.po not in keep_pkgs:
ysp_del_pkg(tspkg)
_ysp_chk_used_map(used_map, lambda x: base.verbose_logger.warn("%s", x))
if cnt:
base.verbose_logger.log(INFO_1, _('%d package(s) needed (+%d related) for security, out of %d available') % (scnt, cnt - scnt, tot))
else:
base.verbose_logger.log(INFO_1, _('No packages needed for security; %d packages available') % tot)
return cnt, scnt, tot
def exclude_updates(base, filters=None):
'''
Exclude all packages to do with updates, using the updateinfo data.
'''
def ysp_del_pkg(pkg, reason="updateinfo"):
""" Deletes a package from all trees that yum knows about """
base.verbose_logger.log(INFO_1,
_(" --> %s from %s excluded (%s)") %
(pkg,pkg.repoid, reason))
pkg.repo.sack.delPackage(pkg)
if filters is None:
filters = base.updateinfo_filters
opts = _ysp_gen_opts(filters)
if _no_options(opts):
return 0, 0
md_info = base.upinfo
used_map = _ysp_gen_used_map(opts)
tot = len(set(base.doPackageLists(pkgnarrow='updates').updates + \
base.doPackageLists(pkgnarrow='obsoletes').obsoletes))
pkgs = base.pkgSack.returnPackages()
name2tup = _get_name2oldpkgtup(base)
pkgs_to_del = []
for pkg in pkgs:
name = pkg.name
if (name not in name2tup or
not _ysp_should_keep_pkg(opts, name2tup[name], md_info, used_map)):
pkgs_to_del.append(pkg.name)
continue
if pkgs_to_del:
for p in base.doPackageLists(pkgnarrow='available', patterns=pkgs_to_del, showdups=True).available:
ysp_del_pkg(p)
cnt = len(set(base.doPackageLists(pkgnarrow='updates').updates + \
base.doPackageLists(pkgnarrow='obsoletes').obsoletes))
_ysp_chk_used_map(used_map, lambda x: base.verbose_logger.warn("%s", x))
if cnt:
base.verbose_logger.log(INFO_1, _('%d package(s) needed for security, out of %d available') % (cnt, tot))
else:
base.verbose_logger.log(INFO_1, _('No packages needed for security; %d packages available' % tot))
return cnt, tot
def exclude_all(base, filters=None):
'''
Exclude all packages, using the updateinfo data.
'''
def ysp_del_pkg(pkg, reason="updateinfo"):
""" Deletes a package from all trees that yum knows about """
base.verbose_logger.log(INFO_1,
_(" --> %s from %s excluded (%s)") %
(pkg,pkg.repoid, reason))
pkg.repo.sack.delPackage(pkg)
if filters is None:
filters = base.updateinfo_filters
opts = _ysp_gen_opts(filters)
if _no_options(opts):
return 0, 0
md_info = base.upinfo
used_map = _ysp_gen_used_map(opts)
pkgs = base.pkgSack.returnPackages()
name2tup = _get_name2aallpkgtup(base)
tot = 0
cnt = 0
for pkg in pkgs:
tot += 1
name = pkg.name
if (name not in name2tup or
not _ysp_should_keep_pkg(opts, name2tup[name], md_info, used_map)):
ysp_del_pkg(pkg)
continue
cnt += 1
_ysp_chk_used_map(used_map, lambda x: base.verbose_logger.warn("%s", x))
if cnt:
base.verbose_logger.log(INFO_1, _('%d package(s) needed for security, out of %d available') % (cnt, tot))
else:
base.verbose_logger.log(INFO_1, _('No packages needed for security; %d packages available' % tot))
return cnt, tot
def update_minimal(base, extcmds=[]):
"""Mark the specified items to be updated, in the minimal way.
:param extcmds: the user specified arguments
:return: a list of transaction members added to the
transaction set by this function
"""
txmbrs = []
used_map = _ysp_gen_used_map(base.updateinfo_filters)
opts = _ysp_gen_opts(base.updateinfo_filters)
ndata = _no_options(opts)
# NOTE: Not doing obsoletes processing atm. ... maybe we should? --
# Also worth pointing out we don't go backwards for obsoletes in the:
# update --security case etc.
# obsoletes = base.up.getObsoletesTuples(newest=False)
# for (obsoleting, installed) in sorted(obsoletes, key=lambda x: x[0]):
# pass
# Tuples == (n, a, e, v, r)
oupdates = map(lambda x: x[1], base.up.getUpdatesTuples())
for oldpkgtup in sorted(oupdates):
data = base.upinfo.get_applicable_notices(oldpkgtup)
if ndata: # No options means pick the oldest update
data.reverse()
for (pkgtup, notice) in data:
name = pkgtup[0]
if extcmds and not _match_sec_cmd(extcmds, name, notice):
continue
if (not ndata and
not _ysp_should_filter_pkg(opts, name, notice, used_map)):
continue
txmbrs.extend(base.update(name=pkgtup[0], arch=pkgtup[1],
epoch=pkgtup[2],
version=pkgtup[3], release=pkgtup[4]))
break
# _ysp_chk_used_map(used_map, msg)
return txmbrs