[ SEA-GHOST MINI SHELL]
#!/usr/bin/python -t
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# Copyright 2002 Duke University
"""
Configuration parser and default values for yum.
"""
_use_iniparse = True
import os
import sys
import warnings
import rpm
import copy
import urlparse
import shlex
from parser import ConfigPreProcessor, varReplace
try:
from iniparse import INIConfig
from iniparse.compat import NoSectionError, NoOptionError, ParsingError
from iniparse.compat import RawConfigParser as ConfigParser
except ImportError:
_use_iniparse = False
if not _use_iniparse:
from ConfigParser import NoSectionError, NoOptionError, ParsingError
from ConfigParser import ConfigParser
import rpmUtils.transaction
import rpmUtils.miscutils
import Errors
import types
from misc import get_uuid, read_in_items_from_dot_dir
import fnmatch
# Alter/patch these to change the default checking...
__pkgs_gpgcheck_default__ = False
__repo_gpgcheck_default__ = False
__payload_gpgcheck_default__ = False
__main_multilib_policy_default__ = 'best'
__main_failovermethod_default__ = 'priority'
__main_installonly_limit_default__ = 3
__group_command_default__ = 'objects'
__exactarchlist_default__ = []
class Option(object):
"""
This class handles a single Yum configuration file option. Create
subclasses for each type of supported configuration option.
Python descriptor foo (__get__ and __set__) is used to make option
definition easy and concise.
"""
def __init__(self, default=None, parse_default=False):
self._setattrname()
self.inherit = False
if parse_default:
default = self.parse(default)
self.default = default
def _setattrname(self):
"""Calculate the internal attribute name used to store option state in
configuration instances.
"""
self._attrname = '__opt%d' % id(self)
def __get__(self, obj, objtype):
"""Called when the option is read (via the descriptor protocol).
:param obj: The configuration instance to modify.
:param objtype: The type of the config instance (not used).
:return: The parsed option value or the default value if the value
wasn't set in the configuration file.
"""
# xemacs highlighting hack: '
if obj is None:
return self
return getattr(obj, self._attrname, None)
def __set__(self, obj, value):
"""Called when the option is set (via the descriptor protocol).
:param obj: The configuration instance to modify.
:param value: The value to set the option to.
"""
# Only try to parse if it's a string
if isinstance(value, basestring):
try:
value = self.parse(value)
except ValueError, e:
# Add the field name onto the error
raise ValueError('Error parsing "%s = %r": %s' % (self._optname,
value, str(e)))
setattr(obj, self._attrname, value)
def setup(self, obj, name):
"""Initialise the option for a config instance.
This must be called before the option can be set or retrieved.
:param obj: :class:`BaseConfig` (or subclass) instance.
:param name: Name of the option.
"""
self._optname = name
setattr(obj, self._attrname, copy.copy(self.default))
def clone(self):
"""Return a safe copy of this :class:`Option` instance.
:return: a safe copy of this :class:`Option` instance
"""
new = copy.copy(self)
new._setattrname()
return new
def parse(self, s):
"""Parse the string value to the :class:`Option`'s native value.
:param s: raw string value to parse
:return: validated native value
:raise: ValueError if there was a problem parsing the string.
Subclasses should override this
"""
return s
def tostring(self, value):
"""Convert the :class:`Option`'s native value to a string value. This
does the opposite of the :func:`parse` method above.
Subclasses should override this.
:param value: native option value
:return: string representation of input
"""
return str(value)
def Inherit(option_obj):
"""Clone an :class:`Option` instance for the purposes of inheritance. The returned
instance has all the same properties as the input :class:`Option` and shares items
such as the default value. Use this to avoid redefinition of reused
options.
:param option_obj: :class:`Option` instance to inherit
:return: New :class:`Option` instance inherited from the input
"""
new_option = option_obj.clone()
new_option.inherit = True
return new_option
class ListOption(Option):
"""An option containing a list of strings."""
def __init__(self, default=None, parse_default=False):
if default is None:
default = []
super(ListOption, self).__init__(default, parse_default)
def parse(self, s):
"""Convert a string from the config file to a workable list, parses
globdir: paths as foo.d-style dirs.
:param s: The string to be converted to a list. Commas and
whitespace are used as separators for the list
:return: *s* converted to a list
"""
# we need to allow for the '\n[whitespace]' continuation - easier
# to sub the \n with a space and then read the lines
s = s.replace('\n', ' ')
s = s.replace(',', ' ')
results = []
for item in s.split():
if item.startswith('glob:'):
thisglob = item.replace('glob:', '')
results.extend(read_in_items_from_dot_dir(thisglob))
continue
results.append(item)
return results
def tostring(self, value):
"""Convert a list of to a string value. This does the
opposite of the :func:`parse` method above.
:param value: a list of values
:return: string representation of input
"""
return '\n '.join(value)
class UrlOption(Option):
"""This option handles lists of URLs with validation of the URL
scheme.
"""
def __init__(self, default=None, schemes=('http', 'ftp', 'file', 'https'),
allow_none=False):
super(UrlOption, self).__init__(default)
self.schemes = schemes
self.allow_none = allow_none
def parse(self, url):
"""Parse a url to make sure that it is valid, and in a scheme
that can be used.
:param url: a string containing the url to parse
:return: *url* if it is valid
:raises: :class:`ValueError` if there is an error parsing the url
"""
url = url.strip()
# Handle the "_none_" special case
if url.lower() == '_none_':
if self.allow_none:
return '_none_'
else:
raise ValueError('"_none_" is not a valid value')
# Check that scheme is valid
(s,b,p,q,f,o) = urlparse.urlparse(url)
if s not in self.schemes:
raise ValueError('URL must be %s not "%s"' % (self._schemelist(), s))
return url
def _schemelist(self):
'''Return a user friendly list of the allowed schemes
'''
if len(self.schemes) < 1:
return 'empty'
elif len(self.schemes) == 1:
return self.schemes[0]
else:
return '%s or %s' % (', '.join(self.schemes[:-1]), self.schemes[-1])
class ProxyOption(UrlOption):
""" Just like URLOption but accept "libproxy" too.
"""
def parse(self, proxy):
if proxy.strip().lower() == 'libproxy':
return 'libproxy'
return UrlOption.parse(self, proxy)
class UrlListOption(ListOption):
"""Option for handling lists of URLs with validation of the URL
scheme.
"""
def __init__(self, default=None, schemes=('http', 'ftp', 'file', 'https'),
parse_default=False):
super(UrlListOption, self).__init__(default, parse_default)
# Hold a UrlOption instance to assist with parsing
self._urloption = UrlOption(schemes=schemes)
def parse(self, s):
"""Parse a string containing multiple urls into a list, and
ensure that they are in a scheme that can be used.
:param s: the string to parse
:return: a list of strings containing the urls in *s*
:raises: :class:`ValueError` if there is an error parsing the urls
"""
out = []
s = s.replace('\n', ' ')
s = s.replace(',', ' ')
items = [ item.replace(' ', '%20') for item in shlex.split(s) ]
tmp = []
for item in items:
if item.startswith('glob:'):
thisglob = item.replace('glob:', '')
tmp.extend(read_in_items_from_dot_dir(thisglob))
continue
tmp.append(item)
for url in super(UrlListOption, self).parse(' '.join(tmp)):
out.append(self._urloption.parse(url))
return out
class WildListOption(ListOption):
"""An option containing a list of strings that supports shell-style
wildcard matching in membership test operations."""
def parse(self, s):
class WildList(list):
def __contains__(self, item):
if not isinstance(item, basestring):
return False
return any(fnmatch.fnmatch(item, p) for p in self)
patterns = super(WildListOption, self).parse(s)
return WildList(patterns)
class IntOption(Option):
"""An option representing an integer value."""
def __init__(self, default=None, range_min=None, range_max=None):
super(IntOption, self).__init__(default)
self._range_min = range_min
self._range_max = range_max
def parse(self, s):
"""Parse a string containing an integer.
:param s: the string to parse
:return: the integer in *s*
:raises: :class:`ValueError` if there is an error parsing the
integer
"""
try:
val = int(s)
except (ValueError, TypeError), e:
raise ValueError('invalid integer value')
if self._range_max is not None and val > self._range_max:
raise ValueError('out of range integer value')
if self._range_min is not None and val < self._range_min:
raise ValueError('out of range integer value')
return val
class PositiveIntOption(IntOption):
"""An option representing a positive integer value, where 0 can
have a special representation.
"""
def __init__(self, default=None, range_min=0, range_max=None,
names_of_0=None):
super(PositiveIntOption, self).__init__(default, range_min, range_max)
self._names0 = names_of_0
def parse(self, s):
"""Parse a string containing a positive integer, where 0 can
have a special representation.
:param s: the string to parse
:return: the integer in *s*
:raises: :class:`ValueError` if there is an error parsing the
integer
"""
if s in self._names0:
return 0
return super(PositiveIntOption, self).parse(s)
class SecondsOption(Option):
"""An option representing an integer value of seconds, or a human
readable variation specifying days, hours, minutes or seconds
until something happens. Works like :class:`BytesOption`. Note
that due to historical president -1 means "never", so this accepts
that and allows the word never, too.
Valid inputs: 100, 1.5m, 90s, 1.2d, 1d, 0xF, 0.1, -1, never.
Invalid inputs: -10, -0.1, 45.6Z, 1d6h, 1day, 1y.
Return value will always be an integer
"""
MULTS = {'d': 60 * 60 * 24, 'h' : 60 * 60, 'm' : 60, 's': 1}
def parse(self, s):
"""Parse a string containing an integer value of seconds, or a human
readable variation specifying days, hours, minutes or seconds
until something happens. Works like :class:`BytesOption`. Note
that due to historical president -1 means "never", so this accepts
that and allows the word never, too.
Valid inputs: 100, 1.5m, 90s, 1.2d, 1d, 0xF, 0.1, -1, never.
Invalid inputs: -10, -0.1, 45.6Z, 1d6h, 1day, 1y.
:param s: the string to parse
:return: an integer representing the number of seconds
specified by *s*
:raises: :class:`ValueError` if there is an error parsing the string
"""
if len(s) < 1:
raise ValueError("no value specified")
if s == "-1" or s == "never": # Special cache timeout, meaning never
return -1
if s[-1].isalpha():
n = s[:-1]
unit = s[-1].lower()
mult = self.MULTS.get(unit, None)
if not mult:
raise ValueError("unknown unit '%s'" % unit)
else:
n = s
mult = 1
try:
n = float(n)
except (ValueError, TypeError), e:
raise ValueError('invalid value')
if n < 0:
raise ValueError("seconds value may not be negative")
return int(n * mult)
class BoolOption(Option):
"""An option representing a boolean value. The value can be one
of 0, 1, yes, no, true, or false.
"""
def parse(self, s):
"""Parse a string containing a boolean value. 1, yes, and
true will evaluate to True; and 0, no, and false will evaluate
to False. Case is ignored.
:param s: the string containing the boolean value
:return: the boolean value contained in *s*
:raises: :class:`ValueError` if there is an error in parsing
the boolean value
"""
s = s.lower()
if s in ('0', 'no', 'false'):
return False
elif s in ('1', 'yes', 'true'):
return True
else:
raise ValueError('invalid boolean value')
def tostring(self, value):
"""Convert a boolean value to a string value. This does the
opposite of the :func:`parse` method above.
:param value: the boolean value to convert
:return: a string representation of *value*
"""
if value:
return "1"
else:
return "0"
class FloatOption(Option):
"""An option representing a numeric float value."""
def parse(self, s):
"""Parse a string containing a numeric float value.
:param s: a string containing a numeric float value to parse
:return: the numeric float value contained in *s*
:raises: :class:`ValueError` if there is an error parsing
float value
"""
try:
return float(s.strip())
except (ValueError, TypeError):
raise ValueError('invalid float value')
class SelectionOption(Option):
"""Handles string values where only specific values are
allowed.
"""
def __init__(self, default=None, allowed=(), mapper={}):
super(SelectionOption, self).__init__(default)
self._allowed = allowed
self._mapper = mapper
def parse(self, s):
"""Parse a string for specific values.
:param s: the string to parse
:return: *s* if it contains a valid value
:raises: :class:`ValueError` if there is an error parsing the values
"""
if s in self._mapper:
s = self._mapper[s]
if s not in self._allowed:
raise ValueError('"%s" is not an allowed value' % s)
return s
class CaselessSelectionOption(SelectionOption):
"""Mainly for compatibility with :class:`BoolOption`, works like
:class:`SelectionOption` but lowers input case.
"""
def parse(self, s):
"""Parse a string for specific values.
:param s: the string to parse
:return: *s* if it contains a valid value
:raises: :class:`ValueError` if there is an error parsing the values
"""
return super(CaselessSelectionOption, self).parse(s.lower())
class BytesOption(Option):
"""An option representing a value in bytes. The value may be given
in bytes, kilobytes, megabytes, or gigabytes.
"""
# Multipliers for unit symbols
MULTS = {
'k': 1024,
'm': 1024*1024,
'g': 1024*1024*1024,
}
def parse(self, s):
"""Parse a friendly bandwidth option to bytes. The input
should be a string containing a (possibly floating point)
number followed by an optional single character unit. Valid
units are 'k', 'M', 'G'. Case is ignored. The convention that
1k = 1024 bytes is used.
Valid inputs: 100, 123M, 45.6k, 12.4G, 100K, 786.3, 0.
Invalid inputs: -10, -0.1, 45.6L, 123Mb.
:param s: the string to parse
:return: the number of bytes represented by *s*
:raises: :class:`ValueError` if the option can't be parsed
"""
if len(s) < 1:
raise ValueError("no value specified")
if s[-1].isalpha():
n = s[:-1]
unit = s[-1].lower()
mult = self.MULTS.get(unit, None)
if not mult:
raise ValueError("unknown unit '%s'" % unit)
else:
n = s
mult = 1
try:
n = float(n)
except ValueError:
raise ValueError("couldn't convert '%s' to number" % n)
if n < 0:
raise ValueError("bytes value may not be negative")
return int(n * mult)
class ThrottleOption(BytesOption):
"""An option representing a bandwidth throttle value. See
:func:`parse` for acceptable input values.
"""
def parse(self, s):
"""Get a throttle option. Input may either be a percentage or
a "friendly bandwidth value" as accepted by the
:class:`BytesOption`.
Valid inputs: 100, 50%, 80.5%, 123M, 45.6k, 12.4G, 100K, 786.0, 0.
Invalid inputs: 100.1%, -4%, -500.
:param s: the string to parse
:return: the bandwidth represented by *s*. The return value
will be an int if a bandwidth value was specified, and a
float if a percentage was given
:raises: :class:`ValueError` if input can't be parsed
"""
if len(s) < 1:
raise ValueError("no value specified")
if s[-1] == '%':
n = s[:-1]
try:
n = float(n)
except ValueError:
raise ValueError("couldn't convert '%s' to number" % n)
if n < 0 or n > 100:
raise ValueError("percentage is out of range")
return n / 100.0
else:
return BytesOption.parse(self, s)
class BaseConfig(object):
"""Base class for storing configuration definitions. Subclass when
creating your own definitions.
"""
def __init__(self):
self._section = None
for name in self.iterkeys():
option = self.optionobj(name)
option.setup(self, name)
def __str__(self):
out = []
out.append('[%s]' % self._section)
for name, value in self.iteritems():
out.append('%s: %r' % (name, value))
return '\n'.join(out)
def populate(self, parser, section, parent=None):
"""Set option values from an INI file section.
:param parser: :class:`ConfigParser` instance (or subclass)
:param section: INI file section to read use
:param parent: Optional parent :class:`BaseConfig` (or
subclass) instance to use when doing option value
inheritance
"""
self.cfg = parser
self._section = section
if parser.has_section(section):
opts = set(parser.options(section))
else:
opts = set()
for name in self.iterkeys():
option = self.optionobj(name)
value = None
if name in opts:
value = parser.get(section, name)
else:
# No matching option in this section, try inheriting
if parent and option.inherit:
value = getattr(parent, name)
if value is not None:
setattr(self, name, value)
def optionobj(cls, name, exceptions=True):
"""Return the :class:`Option` instance for the given name.
:param cls: the class to return the :class:`Option` instance from
:param name: the name of the :class:`Option` instance to return
:param exceptions: defines what action to take if the
specified :class:`Option` instance does not exist. If *exceptions* is
True, a :class:`KeyError` will be raised. If *exceptions*
is False, None will be returned
:return: the :class:`Option` instance specified by *name*, or None if
it does not exist and *exceptions* is False
:raises: :class:`KeyError` if the specified :class:`Option` does not
exist, and *exceptions* is True
"""
obj = getattr(cls, name, None)
if isinstance(obj, Option):
return obj
elif exceptions:
raise KeyError
else:
return None
optionobj = classmethod(optionobj)
def isoption(cls, name):
"""Return True if the given name refers to a defined option.
:param cls: the class to find the option in
:param name: the name of the option to search for
:return: whether *name* specifies a defined option
"""
return cls.optionobj(name, exceptions=False) is not None
isoption = classmethod(isoption)
def iterkeys(self):
"""Yield the names of all defined options in the instance."""
for name in dir(self):
if self.isoption(name):
yield name
def iteritems(self):
"""Yield (name, value) pairs for every option in the
instance. The value returned is the parsed, validated option
value.
"""
# Use dir() so that we see inherited options too
for name in self.iterkeys():
yield (name, getattr(self, name))
def write(self, fileobj, section=None, always=()):
"""Write out the configuration to a file-like object.
:param fileobj: File-like object to write to
:param section: Section name to use. If not specified, the section name
used during parsing will be used
:param always: A sequence of option names to always write out.
Options not listed here will only be written out if they are at
non-default values. Set to None to dump out all options
"""
# Write section heading
if section is None:
if self._section is None:
raise ValueError("not populated, don't know section")
section = self._section
# Updated the ConfigParser with the changed values
cfgOptions = self.cfg.options(section)
for name,value in self.iteritems():
option = self.optionobj(name)
if always is None or name in always or option.default != value or name in cfgOptions :
self.cfg.set(section,name, option.tostring(value))
# write the updated ConfigParser to the fileobj.
self.cfg.write(fileobj)
def getConfigOption(self, option, default=None):
"""Return the current value of the given option.
:param option: string specifying the option to return the
value of
:param default: the value to return if the option does not exist
:return: the value of the option specified by *option*, or
*default* if it does not exist
"""
warnings.warn('getConfigOption() will go away in a future version of Yum.\n'
'Please access option values as attributes or using getattr().',
DeprecationWarning)
if hasattr(self, option):
return getattr(self, option)
return default
def setConfigOption(self, option, value):
"""Set the value of the given option to the given value.
:param option: string specifying the option to set the value
of
:param value: the value to set the option to
"""
warnings.warn('setConfigOption() will go away in a future version of Yum.\n'
'Please set option values as attributes or using setattr().',
DeprecationWarning)
if hasattr(self, option):
setattr(self, option, value)
else:
raise Errors.ConfigError, 'No such option %s' % option
class StartupConf(BaseConfig):
"""Configuration option definitions for yum.conf's [main] section
that are required early in the initialisation process or before
the other [main] options can be parsed.
"""
# xemacs highlighting hack: '
debuglevel = IntOption(2, -4, 10)
errorlevel = IntOption(2, 0, 10)
distroverpkg = ListOption(['system-release(releasever)', 'redhat-release'])
installroot = Option('/')
config_file_path = Option('/etc/yum/yum.conf')
plugins = BoolOption(False)
pluginpath = ListOption(['/usr/share/yum-plugins', '/usr/lib/yum-plugins'])
pluginconfpath = ListOption(['/etc/yum/pluginconf.d'])
gaftonmode = BoolOption(False)
syslog_ident = Option()
syslog_facility = Option('LOG_USER')
syslog_device = Option('/dev/log')
persistdir = Option('/var/lib/yum')
skip_missing_names_on_install = BoolOption(True)
skip_missing_names_on_update = BoolOption(True)
class YumConf(StartupConf):
"""Configuration option definitions for yum.conf's [main] section.
Note: see also options inherited from :class:`StartupConf`
"""
retries = PositiveIntOption(10, names_of_0=["<forever>"])
recent = IntOption(7, range_min=0)
reset_nice = BoolOption(True)
cachedir = Option('/var/cache/yum')
keepcache = BoolOption(True)
usercache = BoolOption(True)
logfile = Option('/var/log/yum.log')
reposdir = ListOption(['/etc/yum/repos.d', '/etc/yum.repos.d'])
commands = ListOption()
exclude = ListOption()
failovermethod = Option(__main_failovermethod_default__)
proxy = ProxyOption(default=False, schemes=('http', 'ftp', 'https',
'socks4', 'socks4a', 'socks5', 'socks5h'), allow_none=True)
proxy_username = Option()
proxy_password = Option()
username = Option()
password = Option()
installonlypkgs = ListOption(['kernel', 'kernel-bigmem',
'installonlypkg(kernel)',
'installonlypkg(kernel-module)',
'installonlypkg(vm)',
'kernel-enterprise','kernel-smp', 'kernel-debug',
'kernel-unsupported', 'kernel-source', 'kernel-devel', 'kernel-PAE',
'kernel-PAE-debug'])
# NOTE: If you set this to 2, then because it keeps the current kernel it
# means if you ever install an "old" kernel it'll get rid of the newest one
# so you probably want to use 3 as a minimum ... if you turn it on.
installonly_limit = PositiveIntOption(__main_installonly_limit_default__,
range_min=2,
names_of_0=["0", "<off>"])
kernelpkgnames = ListOption(['kernel','kernel-smp', 'kernel-enterprise',
'kernel-bigmem', 'kernel-BOOT', 'kernel-PAE', 'kernel-PAE-debug'])
exactarchlist = WildListOption(__exactarchlist_default__)
tsflags = ListOption()
override_install_langs = Option()
assumeyes = BoolOption(False)
assumeno = BoolOption(False)
alwaysprompt = BoolOption(True)
exactarch = BoolOption(True)
tolerant = BoolOption(True)
diskspacecheck = BoolOption(True)
overwrite_groups = BoolOption(False)
keepalive = BoolOption(True)
# FIXME: rename gpgcheck to pkgs_gpgcheck
gpgcheck = BoolOption(__pkgs_gpgcheck_default__)
repo_gpgcheck = BoolOption(__repo_gpgcheck_default__)
localpkg_gpgcheck = BoolOption(__pkgs_gpgcheck_default__)
payload_gpgcheck = BoolOption(__payload_gpgcheck_default__)
obsoletes = BoolOption(True)
showdupesfromrepos = BoolOption(False)
enabled = BoolOption(True)
remove_leaf_only = BoolOption(False)
repopkgsremove_leaf_only = BoolOption(False)
enablegroups = BoolOption(True)
enable_group_conditionals = BoolOption(True)
groupremove_leaf_only = BoolOption(False)
group_package_types = ListOption(['mandatory', 'default'])
group_command = SelectionOption(__group_command_default__,
('compat', 'objects', 'simple'))
upgrade_group_objects_upgrade = BoolOption(True)
timeout = FloatOption(30.0) # FIXME: Should use variation of SecondsOption
minrate = IntOption(0)
bandwidth = BytesOption(0)
throttle = ThrottleOption(0)
ip_resolve = CaselessSelectionOption(
allowed = ('ipv4', 'ipv6', 'whatever'),
mapper = {'4': 'ipv4', '6': 'ipv6'})
max_connections = IntOption(0, range_min=0)
ftp_disable_epsv = BoolOption(False)
deltarpm = IntOption(2, range_min=-16, range_max=128)
deltarpm_percentage = IntOption(75, range_min=0, range_max=100)
deltarpm_metadata_percentage = IntOption(100, range_min=0)
http_caching = SelectionOption('all', ('none', 'packages', 'all',
'lazy:packages'))
metadata_expire = SecondsOption(60 * 60 * 6) # Time in seconds (6h).
metadata_expire_filter = SelectionOption('read-only:present',
('never', 'read-only:future',
'read-only:present',
'read-only:past'))
# Time in seconds (1 day). NOTE: This isn't used when using metalinks
mirrorlist_expire = SecondsOption(60 * 60 * 24)
# XXX rpm_check_debug is unused, left around for API compatibility for now
rpm_check_debug = BoolOption(True)
disable_excludes = ListOption()
query_install_excludes = BoolOption(False)
skip_broken = BoolOption(False)
# Note that "instant" is the old behaviour, but group:primary is very
# similar but better :).
mdpolicy = ListOption(['group:small'])
mddownloadpolicy = SelectionOption('sqlite', ('sqlite', 'xml'))
# ('instant', 'group:all', 'group:main', 'group:small', 'group:primary'))
multilib_policy = SelectionOption(__main_multilib_policy_default__,
('best', 'all'))
# all == install any/all arches you can
# best == use the 'best arch' for the system
bugtracker_url = Option('https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora&version=rawhide&component=yum')
color = SelectionOption('auto', ('auto', 'never', 'always'),
mapper={'on' : 'always', 'yes' : 'always',
'1' : 'always', 'true' : 'always',
'off' : 'never', 'no' : 'never',
'0' : 'never', 'false' : 'never',
'tty' : 'auto', 'if-tty' : 'auto'})
color_list_installed_older = Option('bold')
color_list_installed_newer = Option('bold,yellow')
color_list_installed_reinstall = Option('normal')
color_list_installed_extra = Option('bold,red')
color_list_installed_running_kernel = Option('bold,underline')
color_list_available_upgrade = Option('bold,blue')
color_list_available_downgrade = Option('dim,cyan')
color_list_available_reinstall = Option('bold,underline,green')
color_list_available_install = Option('normal')
color_list_available_running_kernel = Option('bold,underline')
color_update_installed = Option('normal')
color_update_local = Option('bold')
color_update_remote = Option('normal')
color_search_match = Option('bold')
ui_repoid_vars = ListOption(['releasever', 'basearch'])
sslcacert = Option()
sslverify = BoolOption(True)
sslclientcert = Option()
sslclientkey = Option()
ssl_check_cert_permissions = BoolOption(True)
history_record = BoolOption(True)
history_record_packages = ListOption(['yum', 'rpm'])
rpmverbosity = Option('info')
protected_packages = ListOption("yum, glob:/etc/yum/protected.d/*.conf",
parse_default=True)
protected_multilib = BoolOption(True)
exit_on_lock = BoolOption(False)
loadts_ignoremissing = BoolOption(False)
loadts_ignorerpm = BoolOption(False)
loadts_ignorenewrpm = BoolOption(False)
autosavets = BoolOption(True)
clean_requirements_on_remove = BoolOption(False)
upgrade_requirements_on_install = BoolOption(False)
history_list_view = SelectionOption('single-user-commands',
('single-user-commands', 'users',
'commands'),
mapper={'cmds' : 'commands',
'default' :'single-user-commands'})
recheck_installed_requires = BoolOption(False)
fssnap_automatic_pre = BoolOption(False)
fssnap_automatic_post = BoolOption(False)
fssnap_automatic_keep = IntOption(1)
fssnap_percentage = IntOption(100, range_min=1, range_max=100)
fssnap_devices = ListOption("!*/swap !*/lv_swap "
"glob:/etc/yum/fssnap.d/*.conf",
parse_default=True)
fssnap_abort_on_errors = SelectionOption('any', ('broken-setup', 'snapshot-failure', 'any', 'none'))
depsolve_loop_limit = PositiveIntOption(100, names_of_0=["<forever>"])
autocheck_running_kernel = BoolOption(True)
check_config_file_age = BoolOption(True)
usr_w_check = BoolOption(True)
shell_exit_status = SelectionOption('0', ('0', '?'))
_reposlist = []
# cachedir before variable substitutions
_pristine_cachedir = None
def dump(self):
"""Return a string representing the values of all the
configuration options.
:return: a string representing the values of all the
configuration options
"""
output = '[main]\n'
# we exclude all vars which start with _ or are in this list:
excluded_vars = ('cfg', 'uid', 'yumvar', 'progress_obj', 'failure_obj',
'disable_excludes', 'config_file_age', 'config_file_path',
)
for attr in dir(self):
if attr.startswith('_'):
continue
if attr in excluded_vars:
continue
if isinstance(getattr(self, attr), types.MethodType):
continue
res = getattr(self, attr)
if not res and type(res) not in (type(False), type(0)):
res = ''
if type(res) == types.ListType:
res = ',\n '.join(res)
output = output + '%s = %s\n' % (attr, res)
return output
class RepoConf(BaseConfig):
"""Option definitions for repository INI file sections."""
__cached_keys = set()
def iterkeys(self):
"""Yield the names of all defined options in the instance."""
ck = self.__cached_keys
if not isinstance(self, RepoConf):
ck = set()
if not ck:
ck.update(list(BaseConfig.iterkeys(self)))
for name in self.__cached_keys:
yield name
name = Option()
enabled = Inherit(YumConf.enabled)
keepcache = Inherit(YumConf.keepcache)
baseurl = UrlListOption()
mirrorlist = UrlOption()
metalink = UrlOption()
mediaid = Option()
gpgkey = UrlListOption()
gpgcakey = UrlListOption()
exclude = ListOption()
includepkgs = ListOption()
proxy = Inherit(YumConf.proxy)
proxy_username = Inherit(YumConf.proxy_username)
proxy_password = Inherit(YumConf.proxy_password)
retries = Inherit(YumConf.retries)
failovermethod = Inherit(YumConf.failovermethod)
username = Inherit(YumConf.username)
password = Inherit(YumConf.password)
# FIXME: rename gpgcheck to pkgs_gpgcheck
gpgcheck = Inherit(YumConf.gpgcheck)
repo_gpgcheck = Inherit(YumConf.repo_gpgcheck)
keepalive = Inherit(YumConf.keepalive)
enablegroups = Inherit(YumConf.enablegroups)
minrate = Inherit(YumConf.minrate)
bandwidth = Inherit(YumConf.bandwidth)
throttle = Inherit(YumConf.throttle)
timeout = Inherit(YumConf.timeout)
ip_resolve = Inherit(YumConf.ip_resolve)
# This isn't inherited so that we can automatically disable file:// _only_
# repos. if they haven't set an explicit deltarpm_percentage for the repo.
deltarpm_percentage = IntOption(None, range_min=0, range_max=100)
# Rely on the above config. to do automatic disabling, and thus. no hack
# needed here.
deltarpm_metadata_percentage = Inherit(YumConf.deltarpm_metadata_percentage)
ftp_disable_epsv = Inherit(YumConf.ftp_disable_epsv)
http_caching = Inherit(YumConf.http_caching)
metadata_expire = Inherit(YumConf.metadata_expire)
metadata_expire_filter = Inherit(YumConf.metadata_expire_filter)
mirrorlist_expire = Inherit(YumConf.mirrorlist_expire)
# NOTE: metalink expire _must_ be the same as metadata_expire, due to the
# checksumming of the repomd.xml.
mdpolicy = Inherit(YumConf.mdpolicy)
mddownloadpolicy = Inherit(YumConf.mddownloadpolicy)
cost = IntOption(1000)
sslcacert = Inherit(YumConf.sslcacert)
sslverify = Inherit(YumConf.sslverify)
sslclientcert = Inherit(YumConf.sslclientcert)
sslclientkey = Inherit(YumConf.sslclientkey)
ssl_check_cert_permissions = Inherit(YumConf.ssl_check_cert_permissions)
skip_if_unavailable = BoolOption(False)
async = BoolOption(True)
ui_repoid_vars = Inherit(YumConf.ui_repoid_vars)
check_config_file_age = Inherit(YumConf.check_config_file_age)
compare_providers_priority = IntOption(80, range_min=1, range_max=99)
class VersionGroupConf(BaseConfig):
"""Option definitions for version groups."""
pkglist = ListOption()
run_with_packages = BoolOption(False)
def _read_yumvars(yumvars, root):
# Read the FS yumvars
try:
dir_fsvars = root + "/etc/yum/vars/"
fsvars = os.listdir(dir_fsvars)
except OSError:
fsvars = []
for fsvar in fsvars:
if os.path.islink(dir_fsvars + fsvar):
continue
try:
val = open(dir_fsvars + fsvar).readline()
if val and val[-1] == '\n':
val = val[:-1]
except (OSError, IOError):
continue
yumvars[fsvar] = val
def readStartupConfig(configfile, root, releasever=None):
"""Parse Yum's main configuration file and return a
:class:`StartupConf` instance. This is required in order to
access configuration settings required as Yum starts up.
:param configfile: the path to yum.conf
:param root: the base path to use for installation (typically '/')
:return: A :class:`StartupConf` instance
:raises: :class:`Errors.ConfigError` if a problem is detected with while parsing.
"""
# ' xemacs syntax hack
StartupConf.installroot.default = root
startupconf = StartupConf()
startupconf.config_file_path = configfile
parser = ConfigParser()
confpp_obj = ConfigPreProcessor(configfile)
yumvars = _getEnvVar()
_read_yumvars(yumvars, startupconf.installroot)
confpp_obj._vars = yumvars
startupconf.yumvars = yumvars
try:
parser.readfp(confpp_obj)
except ParsingError, e:
raise Errors.ConfigError("Parsing file failed: %s" % e)
startupconf.populate(parser, 'main')
# Check that plugin paths are all absolute
for path in startupconf.pluginpath:
if not path[0] == '/':
raise Errors.ConfigError("All plugin search paths must be absolute")
# Stuff this here to avoid later re-parsing
startupconf._parser = parser
# setup the release ver here
if releasever is None:
releasever = _getsysver(startupconf.installroot,
startupconf.distroverpkg)
startupconf.releasever = releasever
uuidfile = '%s/%s/uuid' % (startupconf.installroot, startupconf.persistdir)
startupconf.uuid = get_uuid(uuidfile)
return startupconf
def readMainConfig(startupconf):
"""Parse Yum's main configuration file
:param startupconf: :class:`StartupConf` instance as returned by readStartupConfig()
:return: Populated :class:`YumConf` instance
"""
# ' xemacs syntax hack
# Set up substitution vars but make sure we always prefer FS yumvars
yumvars = startupconf.yumvars
yumvars.setdefault('basearch', startupconf.basearch)
yumvars.setdefault('arch', startupconf.arch)
yumvars.setdefault('releasever', startupconf.releasever)
yumvars.setdefault('uuid', startupconf.uuid)
# Read [main] section
yumconf = YumConf()
yumconf.populate(startupconf._parser, 'main')
# Store the original cachedir (for later reference in clean commands)
yumconf._pristine_cachedir = yumconf.cachedir
# Apply the installroot to directory options
def _apply_installroot(yumconf, option):
path = getattr(yumconf, option)
ir_path = yumconf.installroot + path
ir_path = ir_path.replace('//', '/') # os.path.normpath won't fix this and
# it annoys me
ir_path = varReplace(ir_path, yumvars)
setattr(yumconf, option, ir_path)
if StartupConf.installroot.default != yumconf.installroot:
# Note that this isn't perfect, in that if the initial installroot has
# X=Y, and X doesn't exist in the new installroot ... then we'll still
# have X afterwards (but if the new installroot has X=Z, that will be
# the value after this).
_read_yumvars(yumvars, yumconf.installroot)
# These can use the above FS yumvars
for option in ('cachedir', 'logfile', 'persistdir'):
_apply_installroot(yumconf, option)
# Add in some extra attributes which aren't actually configuration values
yumconf.yumvar = yumvars
yumconf.uid = 0
yumconf.cache = 0
yumconf.progess_obj = None
# items related to the originating config file
yumconf.config_file_path = startupconf.config_file_path
if os.path.exists(startupconf.config_file_path):
yumconf.config_file_age = os.stat(startupconf.config_file_path)[8]
else:
yumconf.config_file_age = 0
# propagate the debuglevel and errorlevel values:
yumconf.debuglevel = startupconf.debuglevel
yumconf.errorlevel = startupconf.errorlevel
return yumconf
def readVersionGroupsConfig(configfile="/etc/yum/version-groups.conf"):
"""Parse the configuration file for version groups.
:param configfile: the configuration file to read
:return: a dictionary containing the parsed options
"""
parser = ConfigParser()
confpp_obj = ConfigPreProcessor(configfile)
try:
parser.readfp(confpp_obj)
except ParsingError, e:
raise Errors.ConfigError("Parsing file failed: %s" % e)
ret = {}
for section in parser.sections():
ret[section] = VersionGroupConf()
ret[section].populate(parser, section)
return ret
def getOption(conf, section, name, option):
"""Convenience function to retrieve a parsed and converted value from a
:class:`ConfigParser`.
:param conf: ConfigParser instance or similar
:param section: Section name
:param name: :class:`Option` name
:param option: :class:`Option` instance to use for conversion
:return: The parsed value or default if value was not present
:raises: :class:`ValueError` if the option could not be parsed
"""
try:
val = conf.get(section, name)
except (NoSectionError, NoOptionError):
return option.default
return option.parse(val)
def _getEnvVar():
'''Return variable replacements from the environment variables YUM0 to YUM9
The result is intended to be used with parser.varReplace()
'''
yumvar = {}
for num in range(0, 10):
env = 'YUM%d' % num
val = os.environ.get(env, '')
if val:
yumvar[env.lower()] = val
return yumvar
def _getsysver(installroot, distroverpkg):
'''Calculate the release version for the system.
@param installroot: The value of the installroot option.
@param distroverpkg: The value of the distroverpkg option.
@return: The release version as a string (eg. '4' for FC4)
'''
ts = rpmUtils.transaction.initReadOnlyTransaction(root=installroot)
ts.pushVSFlags(~(rpm._RPMVSF_NOSIGNATURES|rpm._RPMVSF_NODIGESTS))
try:
for distroverpkg_prov in distroverpkg:
idx = ts.dbMatch('provides', distroverpkg_prov)
if idx.count():
break
except TypeError, e:
# This is code for "cannot open rpmdb"
# this is for pep 352 compliance on python 2.6 and above :(
if sys.hexversion < 0x02050000:
if hasattr(e,'message'):
raise Errors.YumBaseError("Error: " + str(e.message))
else:
raise Errors.YumBaseError("Error: " + str(e))
raise Errors.YumBaseError("Error: " + str(e))
except rpm.error, e:
# This is the "new" code for "cannot open rpmdb", 4.8.0 ish
raise Errors.YumBaseError("Error: " + str(e))
# we're going to take the first one - if there is more than one of these
# then the user needs a beating
if idx.count() == 0:
releasever = '$releasever'
else:
try:
hdr = idx.next()
except StopIteration:
raise Errors.YumBaseError("Error: rpmdb failed release provides. Try: rpm --rebuilddb")
releasever = hdr['version']
off = hdr[getattr(rpm, 'RPMTAG_PROVIDENAME')].index(distroverpkg_prov)
flag = hdr[getattr(rpm, 'RPMTAG_PROVIDEFLAGS')][off]
flag = rpmUtils.miscutils.flagToString(flag)
ver = hdr[getattr(rpm, 'RPMTAG_PROVIDEVERSION')][off]
if flag == 'EQ' and ver:
if hdr['name'] != distroverpkg_prov:
# override the package version
releasever = ver
del hdr
del idx
del ts
return releasever
def _readRawRepoFile(repo):
if not _use_iniparse:
return None
if not hasattr(repo, 'repofile') or not repo.repofile:
return None
try:
ini = INIConfig(open(repo.repofile))
except:
return None
# b/c repoids can have $values in them we need to map both ways to figure
# out which one is which
section_id = repo.id
if repo.id not in ini._sections:
for sect in ini._sections.keys():
if varReplace(sect, repo.yumvar) == repo.id:
section_id = sect
break
else:
return None
return ini, section_id
def writeRawRepoFile(repo,only=None):
"""Write changes in a repo object back to a .repo file.
:param repo: the Repo Object to write back out
:param only: list of attributes to work on. If *only* is None, all
options will be written out
"""
if not _use_iniparse:
return
ini, section_id = _readRawRepoFile(repo)
# Updated the ConfigParser with the changed values
cfgOptions = repo.cfg.options(repo.id)
for name,value in repo.iteritems():
if value is None: # Proxy
continue
if only is not None and name not in only:
continue
option = repo.optionobj(name)
ovalue = option.tostring(value)
# If the value is the same, but just interpreted ... when we don't want
# to keep the interpreted values.
if (name in ini[section_id] and
ovalue == varReplace(ini[section_id][name], repo.yumvar)):
ovalue = ini[section_id][name]
if name not in cfgOptions and option.default == value:
continue
ini[section_id][name] = ovalue
fp =file(repo.repofile,"w")
fp.write(str(ini))
fp.close()
# Copied from yum-config-manager ... how we alter yu.conf ... used in "yum fs"
def _writeRawConfigFile(filename, section_id, yumvar,
cfgoptions, items, optionobj,
only=None):
"""
From writeRawRepoFile, but so we can alter [main] too.
"""
ini = INIConfig(open(filename))
osection_id = section_id
# b/c repoids can have $values in them we need to map both ways to figure
# out which one is which
if section_id not in ini._sections:
for sect in ini._sections.keys():
if varReplace(sect, yumvar) == section_id:
section_id = sect
# Updated the ConfigParser with the changed values
cfgOptions = cfgoptions(osection_id)
for name,value in items():
if value is None: # Proxy
continue
if only is not None and name not in only:
continue
option = optionobj(name)
ovalue = option.tostring(value)
# If the value is the same, but just interpreted ... when we don't want
# to keep the interpreted values.
if (name in ini[section_id] and
ovalue == varReplace(ini[section_id][name], yumvar)):
ovalue = ini[section_id][name]
if name not in cfgOptions and option.default == value:
continue
ini[section_id][name] = ovalue
fp =file(filename, "w")
fp.write(str(ini))
fp.close()
#def main():
# mainconf = readMainConfig(readStartupConfig('/etc/yum/yum.conf', '/'))
# print mainconf.cachedir
#
#if __name__ == '__main__':
# main()
SEA-GHOST - SHELL CODING BY SEA-GHOST