1430 lines
49 KiB
Python
1430 lines
49 KiB
Python
# -*- coding: utf-8 -*-
|
|
'''
|
|
# DESCRIPTION:
|
|
# Entropy Object Oriented Interface
|
|
|
|
Copyright (C) 2007-2009 Fabio Erculiani
|
|
|
|
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 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
|
|
'''
|
|
# pylint ~ ok
|
|
from __future__ import with_statement
|
|
import os
|
|
from entropy.exceptions import IncorrectParameter, SystemDatabaseError
|
|
from entropy.const import etpConst, etpUi, etpSys, const_setup_perms, \
|
|
const_secure_config_file, const_set_nice_level, \
|
|
const_extract_cli_repo_params, etpCache
|
|
from entropy.i18n import _
|
|
|
|
class Singleton(object):
|
|
|
|
"""
|
|
If your class wants to become a sexy Singleton,
|
|
subclass this and replace __init__ with init_singleton
|
|
"""
|
|
|
|
__is_destroyed = False
|
|
__is_singleton = True
|
|
def __new__(cls, *args, **kwds):
|
|
instance = cls.__dict__.get("__it__")
|
|
if instance != None:
|
|
if not instance.is_destroyed():
|
|
return instance
|
|
cls.__it__ = instance = object.__new__(cls)
|
|
instance.init_singleton(*args, **kwds)
|
|
return instance
|
|
|
|
def is_destroyed(self):
|
|
"""
|
|
In our world, Singleton instances may be destroyed,
|
|
this is done by setting a private bool var __is_destroyed
|
|
|
|
@rtype: bool
|
|
@return: instance status, if destroyed or not
|
|
"""
|
|
return self.__is_destroyed
|
|
|
|
def is_singleton(self):
|
|
"""
|
|
Return if the instance is a singleton
|
|
|
|
@rtype: bool
|
|
@return: class singleton property, if singleton or not
|
|
"""
|
|
return self.__is_singleton
|
|
|
|
class SystemSettingsPlugin:
|
|
|
|
"""
|
|
This is a plugin base class for all SystemSettings plugins.
|
|
It allows to add extra parsers (though metadata) to
|
|
SystemSettings.
|
|
Just inherit from this class and call add_parser to add
|
|
your custom parsers.
|
|
SystemSettings will call the parse method, as explained below.
|
|
"""
|
|
|
|
def __init__(self, plugin_id, helper_interface):
|
|
"""
|
|
SystemSettingsPlugin constructor.
|
|
|
|
@param plugin_id -- plugin identifier, must be unique
|
|
@type plugin_id basestring
|
|
@param helper_interface -- any Python instance that could
|
|
be of help to your parsers
|
|
@type handler_instance instance
|
|
"""
|
|
self.__parsers = []
|
|
self.__plugin_id = plugin_id
|
|
self._helper = helper_interface
|
|
parser_postfix = "_parser"
|
|
for method in sorted(dir(self)):
|
|
if method == "add_parser":
|
|
continue
|
|
elif method.endswith(parser_postfix) and (method != parser_postfix):
|
|
parser_id = method[:len(parser_postfix)*-1]
|
|
self.__parsers.append((parser_id, getattr(self, method),))
|
|
|
|
def get_id(self):
|
|
"""
|
|
Returns the unique plugin id passed at construction time.
|
|
|
|
@return plugin identifier
|
|
"""
|
|
return self.__plugin_id
|
|
|
|
def add_parser(self, parser_id, parser_callable):
|
|
"""
|
|
You must call this method in order to add your custom
|
|
parsers to the plugin.
|
|
Please note, if your parser method ends with "_parser"
|
|
it will be automatically added this way:
|
|
|
|
method: foo_parser
|
|
parser_id => foo
|
|
method: another_fabulous_parser
|
|
parser_id => another_fabulous
|
|
|
|
@param parser_id -- parser identifier, must be unique
|
|
@type parser_id basestring
|
|
@param parser_callable -- any callable function which has
|
|
the following signature: callable(system_settings_instance)
|
|
can return True to stop further parsers calls
|
|
@type parser_callable callable
|
|
"""
|
|
self.__parsers.append((parser_id, parser_callable,))
|
|
|
|
def parse(self, system_settings_instance):
|
|
"""
|
|
This method is called by SystemSettings instance
|
|
when building its settings metadata.
|
|
|
|
Returned data from parser will be put into the SystemSettings
|
|
dict using plugin_id and parser_id keys.
|
|
If returned data is None, SystemSettings dict won't be changed.
|
|
|
|
@param system_settings_instance -- SystemSettings instance
|
|
@type system_settings_instance SystemSettings instance
|
|
"""
|
|
plugin_id = self.get_id()
|
|
for parser_id, parser in self.__parsers:
|
|
data = parser(system_settings_instance)
|
|
if data == None:
|
|
continue
|
|
if not system_settings_instance.has_key(plugin_id):
|
|
system_settings_instance[plugin_id] = {}
|
|
system_settings_instance[plugin_id][parser_id] = data
|
|
|
|
class SystemSettings(Singleton):
|
|
|
|
"""
|
|
This is the place where all the Entropy settings are stored if
|
|
they are not considered instance constants (etpConst).
|
|
For example, here we store package masking cache information and
|
|
settings.
|
|
Also, this class mimics a dictionary (even if not inheriting it
|
|
due to issues with the Singleton class).
|
|
"""
|
|
|
|
import entropy.tools as entropyTools
|
|
def init_singleton(self):
|
|
|
|
"""
|
|
Replaces __init__ because SystemSettings is a Singleton.
|
|
see Singleton API reference for more information.
|
|
|
|
"""
|
|
|
|
from entropy.cache import EntropyCacher
|
|
self.__cacher = EntropyCacher()
|
|
self.__data = {}
|
|
self.__is_destroyed = False
|
|
|
|
self.__plugins = {}
|
|
self.__setting_files_order = []
|
|
self.__setting_files_pre_run = []
|
|
self.__setting_files = {}
|
|
self.__mtime_files = {}
|
|
self.__persistent_settings = {
|
|
'pkg_masking_reasons': {
|
|
0: _('reason not available'),
|
|
1: _('user package.mask'),
|
|
2: _('system keywords'),
|
|
3: _('user package.unmask'),
|
|
4: _('user repo package.keywords (all packages)'),
|
|
5: _('user repo package.keywords'),
|
|
6: _('user package.keywords'),
|
|
7: _('completely masked'),
|
|
8: _('repository general packages.db.mask'),
|
|
10: _('user license.mask'),
|
|
11: _('user live unmask'),
|
|
12: _('user live mask'),
|
|
},
|
|
'pkg_masking_reference': {
|
|
'reason_not_avail': 0,
|
|
'user_package_mask': 1,
|
|
'system_keyword': 2,
|
|
'user_package_unmask': 3,
|
|
'user_repo_package_keywords_all': 4,
|
|
'user_repo_package_keywords': 5,
|
|
'user_package_keywords': 6,
|
|
'completely_masked': 7,
|
|
'repository_packages_db_mask': 8,
|
|
'repository_in_branch_pacakges_db_mask': 9,
|
|
'user_license_mask': 10,
|
|
'user_live_unmask': 11,
|
|
'user_live_mask': 12,
|
|
},
|
|
'backed_up': {},
|
|
# package masking, live
|
|
'live_packagemasking': {
|
|
'unmask_matches': set(),
|
|
'mask_matches': set(),
|
|
},
|
|
}
|
|
|
|
self.__setup_const()
|
|
self.__scan()
|
|
|
|
def destroy(self):
|
|
"""
|
|
Overloaded method from Singleton.
|
|
"Destroys" the instance.
|
|
|
|
@return None
|
|
"""
|
|
self.__is_destroyed = True
|
|
|
|
def add_plugin(self, system_settings_plugin_instance):
|
|
"""
|
|
This method lets you add custom parsers to SystemSettings.
|
|
Mind that you are responsible of handling your plugin instance
|
|
and remove it before it is destroyed. You can remove the plugin
|
|
instance at any time by issuing remove_plugin.
|
|
Every add_plugin or remove_plugin method will also issue clear()
|
|
for you. This could be bad and it might be removed in future.
|
|
|
|
@param plugin_id -- plugin identifier
|
|
@type plugin_id basestring
|
|
@param system_settings_plugin_instance -- valid SystemSettingsPlugin
|
|
instance
|
|
@type system_settings_plugin_instance SystemSettingsPlugin instance
|
|
"""
|
|
inst = system_settings_plugin_instance
|
|
if not isinstance(inst,SystemSettingsPlugin):
|
|
raise AttributeError("SystemSettings: expected valid " + \
|
|
"SystemSettingsPlugin instance")
|
|
self.__plugins[inst.get_id()] = inst
|
|
self.clear()
|
|
|
|
def remove_plugin(self, plugin_id):
|
|
"""
|
|
This method lets you remove previously added custom parsers from
|
|
SystemSettings through its plugin identifier. If plugin_id is not
|
|
available, KeyError exception will be raised.
|
|
Every add_plugin or remove_plugin method will also issue clear()
|
|
for you. This could be bad and it might be removed in future.
|
|
|
|
@param plugin_id -- plugin identifier
|
|
@type plugin_id basestring
|
|
"""
|
|
del self.__plugins[plugin_id]
|
|
self.clear()
|
|
|
|
def __setup_const(self):
|
|
|
|
"""
|
|
Internal method. Does the constants initialization.
|
|
|
|
@return None
|
|
"""
|
|
|
|
del self.__setting_files_order[:]
|
|
del self.__setting_files_pre_run[:]
|
|
self.__setting_files.clear()
|
|
self.__mtime_files.clear()
|
|
|
|
self.__setting_files.update({
|
|
# keywording configuration files
|
|
'keywords': etpConst['confpackagesdir']+"/package.keywords",
|
|
# unmasking configuration files
|
|
'unmask': etpConst['confpackagesdir']+"/package.unmask",
|
|
# masking configuration files
|
|
'mask': etpConst['confpackagesdir']+"/package.mask",
|
|
# masking configuration files
|
|
'license_mask': etpConst['confpackagesdir']+"/license.mask",
|
|
'repos_system_mask': {},
|
|
'system_mask': etpConst['confpackagesdir']+"/system.mask",
|
|
'repos_mask': {},
|
|
'repos_license_whitelist': {},
|
|
'system_package_sets': {},
|
|
'conflicting_tagged_packages': {},
|
|
'system_dirs': etpConst['confdir']+"/fsdirs.conf",
|
|
'system_dirs_mask': etpConst['confdir']+"/fsdirsmask.conf",
|
|
'system_rev_symlinks': etpConst['confdir']+"/fssymlinks.conf",
|
|
'hw_hash': etpConst['confdir']+"/.hw.hash",
|
|
'socket_service': etpConst['socketconf'],
|
|
'system': etpConst['entropyconf'],
|
|
'repositories': etpConst['repositoriesconf'],
|
|
})
|
|
self.__setting_files_order.extend([
|
|
'keywords', 'unmask', 'mask', 'license_mask',
|
|
'repos_system_mask', 'system_mask', 'repos_mask',
|
|
'repos_license_whitelist', 'system_package_sets',
|
|
'conflicting_tagged_packages', 'system_dirs',
|
|
'system_dirs_mask', 'socket_service', 'system',
|
|
'system_rev_symlinks', 'hw_hash'
|
|
])
|
|
self.__setting_files_pre_run.extend(['repositories'])
|
|
|
|
## XXX trunk support, for a while - exp. date 10/10/2009
|
|
trunk_fsdirs_conf = "../conf/fsdirs.conf"
|
|
trunk_fsdirsmask_conf = "../conf/fsdirsmask.conf"
|
|
trunk_fssymlinks_conf = "../conf/fssymlinks.conf"
|
|
if os.path.isfile(trunk_fsdirs_conf):
|
|
self.__setting_files['system_dirs'] = trunk_fsdirs_conf
|
|
if os.path.isfile(trunk_fsdirsmask_conf):
|
|
self.__setting_files['system_dirs_mask'] = trunk_fsdirsmask_conf
|
|
if os.path.isfile(trunk_fssymlinks_conf):
|
|
self.__setting_files['system_rev_symlinks'] = trunk_fssymlinks_conf
|
|
|
|
dmp_dir = etpConst['dumpstoragedir']
|
|
self.__mtime_files.update({
|
|
'keywords_mtime': dmp_dir+"/keywords.mtime",
|
|
'unmask_mtime': dmp_dir+"/unmask.mtime",
|
|
'mask_mtime': dmp_dir+"/mask.mtime",
|
|
'license_mask_mtime': dmp_dir+"/license_mask.mtime",
|
|
'system_mask_mtime': dmp_dir+"/system_mask.mtime",
|
|
'repos_system_mask': {},
|
|
'repos_mask': {},
|
|
'repos_license_whitelist': {},
|
|
})
|
|
|
|
|
|
def __scan(self):
|
|
|
|
"""
|
|
Internal method. Scan settings and fill variables.
|
|
|
|
@return None
|
|
"""
|
|
|
|
self.__parse()
|
|
# merge universal keywords
|
|
for keyword in self.__data['keywords']['universal']:
|
|
etpConst['keywords'].add(keyword)
|
|
|
|
# plugins support
|
|
for plugin_id in sorted(self.__plugins):
|
|
self.__plugins[plugin_id].parse(self)
|
|
|
|
# merge persistent settings back
|
|
self.__data.update(self.__persistent_settings)
|
|
# restore backed-up settings
|
|
self.__data.update(self.__persistent_settings['backed_up'].copy())
|
|
|
|
def __setitem__(self, mykey, myvalue):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
if self.__persistent_settings.has_key(mykey): # backup here too
|
|
self.__persistent_settings[mykey] = myvalue
|
|
self.__data[mykey] = myvalue
|
|
|
|
def __getitem__(self, mykey):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
return self.__data[mykey]
|
|
|
|
def __delitem__(self, mykey):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
del self.__data[mykey]
|
|
|
|
def __iter__(self):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
return iter(self.__data)
|
|
|
|
def __contains__(self, item):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
return item in self.__data
|
|
|
|
def __cmp__(self, other):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
return cmp(self.__data, other)
|
|
|
|
def __hash__(self):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
return hash(self.__data)
|
|
|
|
def __len__(self):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
return len(self.__data)
|
|
|
|
def get(self, mykey):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
return self.__data.get(mykey)
|
|
|
|
def has_key(self, mykey):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
return self.__data.has_key(mykey)
|
|
|
|
def copy(self):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
return self.__data.copy()
|
|
|
|
def fromkeys(self, seq, val = None):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
return self.__data.fromkeys(seq, val)
|
|
|
|
def items(self):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
return self.__data.items()
|
|
|
|
def iteritems(self):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
return self.__data.iteritems()
|
|
|
|
def iterkeys(self):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
return self.__data.iterkeys()
|
|
|
|
def keys(self):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
return self.__data.keys()
|
|
|
|
def pop(self, mykey, default = None):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
return self.__data.pop(mykey, default)
|
|
|
|
def popitem(self):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
return self.__data.popitem()
|
|
|
|
def setdefault(self, mykey, default = None):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
return self.__data.setdefault(mykey, default)
|
|
|
|
def update(self, kwargs):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
return self.__data.update(kwargs)
|
|
|
|
def values(self):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
"""
|
|
return self.__data.values()
|
|
|
|
def clear(self):
|
|
"""
|
|
dict method. See Python dict API reference.
|
|
Settings are also re-initialized here.
|
|
|
|
@return None
|
|
"""
|
|
self.__data.clear()
|
|
self.__setup_const()
|
|
self.__scan()
|
|
|
|
def set_persistent_setting(self, persistent_dict):
|
|
"""
|
|
Make metadata persistent, the input dict will be merged
|
|
with the base one at every reset call (clear()).
|
|
|
|
@param persistent_dict -- dictionary to merge
|
|
@type persistent_dict dict
|
|
|
|
@return None
|
|
"""
|
|
self.__persistent_settings.update(persistent_dict)
|
|
|
|
def unset_persistent_setting(self, persistent_key):
|
|
"""
|
|
Remove dict key from persistent dictionary
|
|
|
|
@param persistent_key -- key to remove
|
|
@type persistent_dict dict
|
|
|
|
@return None
|
|
"""
|
|
del self.__persistent_settings[persistent_key]
|
|
del self.__data[persistent_key]
|
|
|
|
def __setup_setting_vars(self):
|
|
|
|
"""
|
|
This function setups the *mtimes* and *files* dictionaries
|
|
that will be read and parsed afterwards by respective
|
|
internal parsers.
|
|
|
|
@return None
|
|
"""
|
|
|
|
dmp_dir = etpConst['dumpstoragedir']
|
|
for repoid in self['repositories']['order']:
|
|
|
|
repos_mask_setting = {}
|
|
repos_mask_mtime = {}
|
|
repos_lic_wl_setting = {}
|
|
repos_lic_wl_mtime = {}
|
|
repo_data = self['repositories']['available'][repoid]
|
|
repos_sm_mask_setting = {}
|
|
repos_sm_mask_mtime = {}
|
|
confl_tagged = {}
|
|
|
|
maskpath = os.path.join(repo_data['dbpath'],
|
|
etpConst['etpdatabasemaskfile'])
|
|
wlpath = os.path.join(repo_data['dbpath'],
|
|
etpConst['etpdatabaselicwhitelistfile'])
|
|
sm_path = os.path.join(repo_data['dbpath'],
|
|
etpConst['etpdatabasesytemmaskfile'])
|
|
ct_path = os.path.join(repo_data['dbpath'],
|
|
etpConst['etpdatabaseconflictingtaggedfile'])
|
|
|
|
if os.path.isfile(maskpath) and os.access(maskpath, os.R_OK):
|
|
repos_mask_setting[repoid] = maskpath
|
|
repos_mask_mtime[repoid] = dmp_dir + "/repo_" + \
|
|
repoid + "_" + etpConst['etpdatabasemaskfile'] + ".mtime"
|
|
|
|
if os.path.isfile(wlpath) and os.access(wlpath, os.R_OK):
|
|
repos_lic_wl_setting[repoid] = wlpath
|
|
repos_lic_wl_mtime[repoid] = dmp_dir + "/repo_" + \
|
|
repoid + "_" + etpConst['etpdatabaselicwhitelistfile'] + \
|
|
".mtime"
|
|
|
|
if os.path.isfile(sm_path) and os.access(sm_path, os.R_OK):
|
|
repos_sm_mask_setting[repoid] = sm_path
|
|
repos_sm_mask_mtime[repoid] = dmp_dir + "/repo_" + \
|
|
repoid + "_" + etpConst['etpdatabasesytemmaskfile'] + \
|
|
".mtime"
|
|
if os.path.isfile(ct_path) and os.access(ct_path, os.R_OK):
|
|
confl_tagged[repoid] = ct_path
|
|
|
|
self.__setting_files['repos_mask'].update(repos_mask_setting)
|
|
self.__mtime_files['repos_mask'].update(repos_mask_mtime)
|
|
|
|
self.__setting_files['repos_license_whitelist'].update(
|
|
repos_lic_wl_setting)
|
|
self.__mtime_files['repos_license_whitelist'].update(
|
|
repos_lic_wl_mtime)
|
|
|
|
self.__setting_files['repos_system_mask'].update(
|
|
repos_sm_mask_setting)
|
|
self.__mtime_files['repos_system_mask'].update(
|
|
repos_sm_mask_mtime)
|
|
|
|
self.__setting_files['conflicting_tagged_packages'].update(
|
|
confl_tagged)
|
|
|
|
def __setup_package_sets_vars(self):
|
|
|
|
"""
|
|
This function setups the *files* dictionary about package sets
|
|
that will be read and parsed afterwards by the respective
|
|
internal parser.
|
|
|
|
@return None
|
|
"""
|
|
|
|
# user defined package sets
|
|
sets_dir = etpConst['confsetsdir']
|
|
pkg_set_data = {}
|
|
if (os.path.isdir(sets_dir) and os.access(sets_dir, os.R_OK)):
|
|
set_files = [x for x in os.listdir(sets_dir) if \
|
|
(os.path.isfile(os.path.join(sets_dir, x)) and \
|
|
os.access(os.path.join(sets_dir, x),os.R_OK))]
|
|
for set_file in set_files:
|
|
try:
|
|
set_file = str(set_file)
|
|
except (UnicodeDecodeError, UnicodeEncodeError,):
|
|
continue
|
|
pkg_set_data[set_file] = os.path.join(sets_dir, set_file)
|
|
self.__setting_files['system_package_sets'].update(pkg_set_data)
|
|
|
|
def __parse(self):
|
|
"""
|
|
This is the main internal parsing method.
|
|
*files* and *mtimes* dictionaries are prepared and
|
|
parsed just a few lines later.
|
|
|
|
@return dict settings metadata
|
|
"""
|
|
# some parsers must be run BEFORE everything:
|
|
for item in self.__setting_files_pre_run:
|
|
myattr = '%s_parser' % (item,)
|
|
if not hasattr(self, myattr):
|
|
continue
|
|
func = getattr(self, myattr)
|
|
self.__data[item] = func()
|
|
|
|
# parse main settings
|
|
self.__setup_setting_vars()
|
|
self.__setup_package_sets_vars()
|
|
|
|
data = {}
|
|
for item in self.__setting_files_order:
|
|
myattr = '%s_parser' % (item,)
|
|
if not hasattr(self, myattr):
|
|
continue
|
|
func = getattr(self, myattr)
|
|
self.__data[item] = func()
|
|
|
|
def get_setting_files_data(self):
|
|
"""
|
|
Return a copy of the internal *files* dictionary.
|
|
This dict contains config file paths and their identifiers.
|
|
|
|
@return dict __setting_files
|
|
"""
|
|
return self.__setting_files.copy()
|
|
|
|
def get_mtime_files_data(self):
|
|
"""
|
|
Return a copy of the internal *mtime* dictionary.
|
|
This dict contains config file paths and their current mtime.
|
|
|
|
@return dict __mtime_files
|
|
"""
|
|
return self.__mtime_files.copy()
|
|
|
|
def keywords_parser(self):
|
|
"""
|
|
Parser returning package keyword masking metadata
|
|
read from package.keywords file.
|
|
This file contains package mask or unmask directives
|
|
based on package keywords.
|
|
|
|
@return dict data
|
|
"""
|
|
data = {
|
|
'universal': set(),
|
|
'packages': {},
|
|
'repositories': {},
|
|
}
|
|
|
|
self.__validate_entropy_cache(self.__setting_files['keywords'],
|
|
self.__mtime_files['keywords_mtime'])
|
|
content = [x.split() for x in \
|
|
self.__generic_parser(self.__setting_files['keywords']) \
|
|
if len(x.split()) < 4]
|
|
for keywordinfo in content:
|
|
# skip wrong lines
|
|
if len(keywordinfo) > 3:
|
|
continue
|
|
# inversal keywording, check if it's not repo=
|
|
if len(keywordinfo) == 1:
|
|
if keywordinfo[0].startswith("repo="):
|
|
continue
|
|
# convert into entropy format
|
|
if keywordinfo[0] == "**":
|
|
keywordinfo[0] = ""
|
|
data['universal'].add(keywordinfo[0])
|
|
continue
|
|
# inversal keywording, check if it's not repo=
|
|
if len(keywordinfo) in (2, 3,):
|
|
# repo=?
|
|
if keywordinfo[0].startswith("repo="):
|
|
continue
|
|
# add to repo?
|
|
items = keywordinfo[1:]
|
|
# convert into entropy format
|
|
if keywordinfo[0] == "**":
|
|
keywordinfo[0] = ""
|
|
reponame = [x for x in items if x.startswith("repo=") \
|
|
and (len(x.split("=")) == 2)]
|
|
if reponame:
|
|
reponame = reponame[0].split("=")[1]
|
|
if reponame not in data['repositories']:
|
|
data['repositories'][reponame] = {}
|
|
# repository unmask or package in repository unmask?
|
|
if keywordinfo[0] not in data['repositories'][reponame]:
|
|
data['repositories'][reponame][keywordinfo[0]] = set()
|
|
if len(items) == 1:
|
|
# repository unmask
|
|
data['repositories'][reponame][keywordinfo[0]].add('*')
|
|
elif "*" not in \
|
|
data['repositories'][reponame][keywordinfo[0]]:
|
|
|
|
item = [x for x in items if not x.startswith("repo=")]
|
|
data['repositories'][reponame][keywordinfo[0]].add(
|
|
item[0])
|
|
elif len(items) == 2:
|
|
# it's going to be a faulty line!!??
|
|
# can't have two items and no repo=
|
|
continue
|
|
else:
|
|
# add keyword to packages
|
|
if keywordinfo[0] not in data['packages']:
|
|
data['packages'][keywordinfo[0]] = set()
|
|
data['packages'][keywordinfo[0]].add(items[0])
|
|
return data
|
|
|
|
|
|
def unmask_parser(self):
|
|
"""
|
|
Parser returning package unmasking metadata read from
|
|
package.unmask file.
|
|
This file contains package unmask directives, allowing
|
|
to enable experimental or *secret* packages.
|
|
|
|
@return list parsed data
|
|
"""
|
|
self.__validate_entropy_cache(self.__setting_files['unmask'],
|
|
self.__mtime_files['unmask_mtime'])
|
|
return self.__generic_parser(self.__setting_files['unmask'])
|
|
|
|
def mask_parser(self):
|
|
"""
|
|
Parser returning package masking metadata read from
|
|
package.mask file.
|
|
This file contains package mask directives, allowing
|
|
to disable experimental or *secret* packages.
|
|
|
|
@return list parsed data
|
|
"""
|
|
self.__validate_entropy_cache(self.__setting_files['mask'],
|
|
self.__mtime_files['mask_mtime'])
|
|
return self.__generic_parser(self.__setting_files['mask'])
|
|
|
|
def system_mask_parser(self):
|
|
"""
|
|
Parser returning system packages mask metadata read from
|
|
package.system_mask file.
|
|
This file contains packages that should be always kept
|
|
installed, extending the already defined (in repository database)
|
|
set of atoms.
|
|
|
|
@return list parsed data
|
|
"""
|
|
self.__validate_entropy_cache(self.__setting_files['system_mask'],
|
|
self.__mtime_files['system_mask_mtime'])
|
|
return self.__generic_parser(self.__setting_files['system_mask'])
|
|
|
|
def license_mask_parser(self):
|
|
"""
|
|
Parser returning packages masked by license metadata read from
|
|
license.mask file.
|
|
Packages shipped with licenses listed there will be masked.
|
|
|
|
@return list parsed data
|
|
"""
|
|
self.__validate_entropy_cache(self.__setting_files['license_mask'],
|
|
self.__mtime_files['license_mask_mtime'])
|
|
return self.__generic_parser(self.__setting_files['license_mask'])
|
|
|
|
def repos_license_whitelist_parser(self):
|
|
"""
|
|
Parser returning licenses considered accepted by default
|
|
(= GPL compatibles) read from package.lic_whitelist.
|
|
|
|
@return dict parsed data
|
|
"""
|
|
data = {}
|
|
for repoid in self.__setting_files['repos_license_whitelist']:
|
|
self.__validate_entropy_cache(
|
|
self.__setting_files['repos_license_whitelist'][repoid],
|
|
self.__mtime_files['repos_license_whitelist'][repoid],
|
|
repoid = repoid)
|
|
data[repoid] = self.__generic_parser(
|
|
self.__setting_files['repos_license_whitelist'][repoid])
|
|
return data
|
|
|
|
def repos_mask_parser(self):
|
|
"""
|
|
Parser returning packages masked at repository level read from
|
|
packages.db.mask inside the repository database directory.
|
|
|
|
@return dict parsed data
|
|
"""
|
|
data = {}
|
|
for repoid in self.__setting_files['repos_mask']:
|
|
self.__validate_entropy_cache(
|
|
self.__setting_files['repos_mask'][repoid],
|
|
self.__mtime_files['repos_mask'][repoid], repoid = repoid)
|
|
data[repoid] = self.__generic_parser(
|
|
self.__setting_files['repos_mask'][repoid])
|
|
# why ? line = line.split()[0] in the previous one?
|
|
return data
|
|
|
|
def repos_system_mask_parser(self):
|
|
"""
|
|
Parser returning system packages mask metadata read from
|
|
packages.db.system_mask file inside the repository directory.
|
|
This file contains packages that should be always kept
|
|
installed, extending the already defined (in repository database)
|
|
set of atoms.
|
|
|
|
@return dict parsed data
|
|
"""
|
|
data = []
|
|
for repoid in self.__setting_files['repos_system_mask']:
|
|
self.__validate_entropy_cache(
|
|
self.__setting_files['repos_system_mask'][repoid],
|
|
self.__mtime_files['repos_system_mask'][repoid],
|
|
repoid = repoid)
|
|
data += [x for x in self.__generic_parser(
|
|
self.__setting_files['repos_system_mask'][repoid]) if x \
|
|
not in data]
|
|
# why ? line = line.split()[0] in the previous one?
|
|
return data
|
|
|
|
def system_package_sets_parser(self):
|
|
"""
|
|
Parser returning system defined package sets read from
|
|
/etc/entropy/packages/sets.
|
|
|
|
@return dict parsed data
|
|
"""
|
|
data = {}
|
|
for set_name in self.__setting_files['system_package_sets']:
|
|
set_filepath = self.__setting_files['system_package_sets'][set_name]
|
|
set_elements = self.entropyTools.extract_packages_from_set_file(
|
|
set_filepath)
|
|
if set_elements:
|
|
data[set_name] = set_elements.copy()
|
|
return data
|
|
|
|
def system_dirs_parser(self):
|
|
"""
|
|
Parser returning directories considered part of the base system.
|
|
|
|
@return list parsed data
|
|
"""
|
|
return self.__generic_parser(self.__setting_files['system_dirs'])
|
|
|
|
def system_dirs_mask_parser(self):
|
|
"""
|
|
Parser returning directories NOT considered part of the base system.
|
|
Settings here overlay system_dirs_parser.
|
|
|
|
@return list parsed data
|
|
"""
|
|
return self.__generic_parser(self.__setting_files['system_dirs_mask'])
|
|
|
|
def hw_hash_parser(self):
|
|
"""
|
|
Hardware hash metadata parser and generator. It returns a theorically
|
|
unique SHA256 hash bound to the computer running this Framework.
|
|
|
|
@return basestring containing SHA256 hexdigest
|
|
"""
|
|
hw_hash_file = self.__setting_files['hw_hash']
|
|
if os.access(hw_hash_file, os.R_OK | os.F_OK):
|
|
hash_f = open(hw_hash_file, "r")
|
|
hash_data = hash_f.readline().strip()
|
|
hash_f.close()
|
|
return hash_data
|
|
|
|
hash_file_dir = os.path.dirname(hw_hash_file)
|
|
hw_hash_exec = etpConst['etp_hw_hash_gen']
|
|
if os.access(hash_file_dir, os.W_OK) and \
|
|
os.access(hw_hash_exec, os.X_OK | os.F_OK | os.R_OK):
|
|
pipe = os.popen('{ ' + hw_hash_exec + '; } 2>&1', 'r')
|
|
hash_data = pipe.read().strip()
|
|
sts = pipe.close()
|
|
if sts != None:
|
|
return None
|
|
hash_f = open(hw_hash_file, "w")
|
|
hash_f.write(hash_data)
|
|
hash_f.flush()
|
|
hash_f.close()
|
|
return hash_data
|
|
|
|
def system_rev_symlinks_parser(self):
|
|
"""
|
|
Parser returning important system symlinks mapping. For example:
|
|
{'/usr/lib': ['/usr/lib64']}
|
|
Useful for reverse matching files belonging to packages.
|
|
|
|
@return dict containing the mapping
|
|
"""
|
|
setting_file = self.__setting_files['system_rev_symlinks']
|
|
raw_data = self.__generic_parser(setting_file)
|
|
data = {}
|
|
for line in raw_data:
|
|
line = line.split()
|
|
if len(line) < 2:
|
|
continue
|
|
data[line[0]] = frozenset(line[1:])
|
|
return data
|
|
|
|
def conflicting_tagged_packages_parser(self):
|
|
"""
|
|
Parser returning packages that could have been installed because
|
|
they aren't in the same scope, but ending up creating critical
|
|
issues. You can see it as a configurable conflict map.
|
|
|
|
@return dict parsed data
|
|
"""
|
|
data = {}
|
|
# keep priority order
|
|
repoids = [x for x in self['repositories']['order'] if x in \
|
|
self.__setting_files['conflicting_tagged_packages']]
|
|
for repoid in repoids:
|
|
filepath = self.__setting_files['conflicting_tagged_packages'].get(
|
|
repoid)
|
|
if os.path.isfile(filepath) and os.access(filepath, os.R_OK):
|
|
confl_f = open(filepath,"r")
|
|
content = confl_f.readlines()
|
|
confl_f.close()
|
|
content = [x.strip().rsplit("#", 1)[0].strip().split() for x \
|
|
in content if not x.startswith("#") and x.strip()]
|
|
for mydata in content:
|
|
if len(mydata) < 2:
|
|
continue
|
|
data[mydata[0]] = mydata[1:]
|
|
return data
|
|
|
|
def socket_service_parser(self):
|
|
"""
|
|
Parses socket service configuration file.
|
|
This file contains information about Entropy remote service ports
|
|
and SSL.
|
|
|
|
@return dict data
|
|
"""
|
|
|
|
data = etpConst['socket_service'].copy()
|
|
|
|
sock_conf = self.__setting_files['socket_service']
|
|
if not (os.path.isfile(sock_conf) and \
|
|
os.access(sock_conf,os.R_OK)):
|
|
return data
|
|
|
|
socket_f = open(sock_conf,"r")
|
|
socketconf = [x.strip() for x in socket_f.readlines() if \
|
|
x.strip() and not x.strip().startswith("#")]
|
|
socket_f.close()
|
|
|
|
for line in socketconf:
|
|
|
|
split_line = line.split("|")
|
|
split_line_len = len(split_line)
|
|
|
|
if line.startswith("listen|") and (split_line_len > 1):
|
|
|
|
item = split_line[1].strip()
|
|
if item:
|
|
data['hostname'] = item
|
|
|
|
elif line.startswith("listen-port|") and \
|
|
(split_line_len > 1):
|
|
|
|
item = split_line[1].strip()
|
|
try:
|
|
item = int(item)
|
|
data['port'] = item
|
|
except ValueError:
|
|
continue
|
|
|
|
elif line.startswith("listen-timeout|") and \
|
|
(split_line_len > 1):
|
|
|
|
item = split_line[1].strip()
|
|
try:
|
|
item = int(item)
|
|
data['timeout'] = item
|
|
except ValueError:
|
|
continue
|
|
|
|
elif line.startswith("listen-threads|") and \
|
|
(split_line_len > 1):
|
|
|
|
item = split_line[1].strip()
|
|
try:
|
|
item = int(item)
|
|
data['threads'] = item
|
|
except ValueError:
|
|
continue
|
|
|
|
elif line.startswith("session-ttl|") and \
|
|
(split_line_len > 1):
|
|
|
|
item = split_line[1].strip()
|
|
try:
|
|
item = int(item)
|
|
data['session_ttl'] = item
|
|
except ValueError:
|
|
continue
|
|
|
|
elif line.startswith("max-connections|") and \
|
|
(split_line_len > 1):
|
|
|
|
item = split_line[1].strip()
|
|
try:
|
|
item = int(item)
|
|
data['max_connections'] = item
|
|
except ValueError:
|
|
continue
|
|
|
|
elif line.startswith("ssl-port|") and \
|
|
(split_line_len > 1):
|
|
|
|
item = split_line[1].strip()
|
|
try:
|
|
item = int(item)
|
|
data['ssl_port'] = item
|
|
except ValueError:
|
|
continue
|
|
|
|
elif line.startswith("disabled-commands|") and \
|
|
(split_line_len > 1):
|
|
|
|
disabled_cmds = split_line[1].strip().split()
|
|
for disabled_cmd in disabled_cmds:
|
|
data['disabled_cmds'].add(disabled_cmd)
|
|
|
|
elif line.startswith("ip-blacklist|") and \
|
|
(split_line_len > 1):
|
|
|
|
ips_blacklist = split_line[1].strip().split()
|
|
for ip_blacklist in ips_blacklist:
|
|
data['ip_blacklist'].add(ip_blacklist)
|
|
|
|
return data
|
|
|
|
def system_parser(self):
|
|
|
|
"""
|
|
Parses Entropy system configuration file.
|
|
|
|
@return dict data
|
|
"""
|
|
|
|
data = {}
|
|
data['proxy'] = etpConst['proxy'].copy()
|
|
data['name'] = etpConst['systemname']
|
|
data['log_level'] = etpConst['entropyloglevel']
|
|
|
|
etp_conf = self.__setting_files['system']
|
|
if not os.path.isfile(etp_conf) and \
|
|
os.access(etp_conf,os.R_OK):
|
|
return data
|
|
|
|
const_secure_config_file(etp_conf)
|
|
entropy_f = open(etp_conf,"r")
|
|
entropyconf = [x.strip() for x in entropy_f.readlines() if \
|
|
x.strip() and not x.strip().startswith("#")]
|
|
entropy_f.close()
|
|
|
|
for line in entropyconf:
|
|
|
|
split_line = line.split("|")
|
|
split_line_len = len(split_line)
|
|
|
|
if line.startswith("loglevel|") and \
|
|
(len(line.split("loglevel|")) == 2):
|
|
|
|
loglevel = line.split("loglevel|")[1]
|
|
try:
|
|
loglevel = int(loglevel)
|
|
except ValueError:
|
|
pass
|
|
if (loglevel > -1) and (loglevel < 3):
|
|
data['log_level'] = loglevel
|
|
|
|
elif line.startswith("ftp-proxy|") and \
|
|
(split_line_len == 2):
|
|
|
|
ftpproxy = split_line[1].strip().split()
|
|
if ftpproxy:
|
|
data['proxy']['ftp'] = ftpproxy[-1]
|
|
|
|
elif line.startswith("http-proxy|") and \
|
|
(split_line_len == 2):
|
|
|
|
httpproxy = split_line[1].strip().split()
|
|
if httpproxy:
|
|
data['proxy']['http'] = httpproxy[-1]
|
|
|
|
elif line.startswith("proxy-username|") and \
|
|
(split_line_len == 2):
|
|
|
|
httpproxy = split_line[1].strip().split()
|
|
if httpproxy:
|
|
data['proxy']['username'] = httpproxy[-1]
|
|
|
|
elif line.startswith("proxy-password|") and \
|
|
(split_line_len == 2):
|
|
|
|
httpproxy = split_line[1].strip().split()
|
|
if httpproxy:
|
|
data['proxy']['password'] = httpproxy[-1]
|
|
|
|
elif line.startswith("system-name|") and \
|
|
(split_line_len == 2):
|
|
|
|
data['name'] = split_line[1].strip()
|
|
|
|
elif line.startswith("nice-level|") and \
|
|
(split_line_len == 2):
|
|
|
|
mylevel = split_line[1].strip()
|
|
try:
|
|
mylevel = int(mylevel)
|
|
if (mylevel >= -19) and (mylevel <= 19):
|
|
const_set_nice_level(mylevel)
|
|
except (ValueError,):
|
|
continue
|
|
|
|
return data
|
|
|
|
def repositories_parser(self):
|
|
|
|
"""
|
|
Setup Entropy Client repository settings reading them from
|
|
the relative config file specified in etpConst['repositoriesconf']
|
|
|
|
@return None
|
|
"""
|
|
|
|
data = {
|
|
'available': {},
|
|
'excluded': {},
|
|
'order': [],
|
|
'product': etpConst['product'],
|
|
'branch': etpConst['branch'],
|
|
'default_repository': etpConst['officialrepositoryid'],
|
|
'transfer_limit': etpConst['downloadspeedlimit'],
|
|
'security_advisories_url': etpConst['securityurl'],
|
|
}
|
|
|
|
repo_conf = etpConst['repositoriesconf']
|
|
if not (os.path.isfile(repo_conf) and os.access(repo_conf, os.R_OK)):
|
|
return data
|
|
|
|
repo_f = open(repo_conf,"r")
|
|
repositoriesconf = [x.strip() for x in repo_f.readlines() if x.strip()]
|
|
repo_f.close()
|
|
|
|
# setup product and branch first
|
|
for line in repositoriesconf:
|
|
|
|
split_line = line.split("|")
|
|
split_line_len = len(split_line)
|
|
|
|
if (line.find("product|") != -1) and \
|
|
(not line.startswith("#")) and (split_line_len == 2):
|
|
|
|
data['product'] = split_line[1]
|
|
|
|
elif (line.find("branch|") != -1) and \
|
|
(not line.startswith("#")) and (split_line_len == 2):
|
|
|
|
branch = split_line[1].strip()
|
|
data['branch'] = branch
|
|
if not os.path.isdir(etpConst['packagesbindir']+"/"+branch) \
|
|
and (etpConst['uid'] == 0):
|
|
|
|
try:
|
|
os.makedirs(etpConst['packagesbindir']+"/"+branch)
|
|
except (OSError, IOError,):
|
|
continue
|
|
|
|
for line in repositoriesconf:
|
|
|
|
split_line = line.split("|")
|
|
split_line_len = len(split_line)
|
|
|
|
# populate data['available']
|
|
if (line.find("repository|") != -1) and (split_line_len == 5):
|
|
|
|
excluded = False
|
|
my_repodata = data['available']
|
|
if line.startswith("##"):
|
|
continue
|
|
elif line.startswith("#"):
|
|
excluded = True
|
|
my_repodata = data['excluded']
|
|
line = line[1:]
|
|
|
|
reponame, repodata = const_extract_cli_repo_params(line,
|
|
data['branch'], data['product'])
|
|
if my_repodata.has_key(reponame):
|
|
|
|
my_repodata[reponame]['plain_packages'].extend(
|
|
repodata['plain_packages'])
|
|
my_repodata[reponame]['packages'].extend(
|
|
repodata['packages'])
|
|
|
|
if (not my_repodata[reponame]['plain_database']) and \
|
|
repodata['plain_database']:
|
|
|
|
my_repodata[reponame]['plain_database'] = \
|
|
repodata['plain_database']
|
|
my_repodata[reponame]['database'] = \
|
|
repodata['database']
|
|
my_repodata[reponame]['dbrevision'] = \
|
|
repodata['dbrevision']
|
|
my_repodata[reponame]['dbcformat'] = \
|
|
repodata['dbcformat']
|
|
else:
|
|
|
|
my_repodata[reponame] = repodata.copy()
|
|
if not excluded:
|
|
data['order'].append(reponame)
|
|
|
|
elif (line.find("officialrepositoryid|") != -1) and \
|
|
(not line.startswith("#")) and (split_line_len == 2):
|
|
|
|
officialreponame = split_line[1]
|
|
data['default_repository'] = officialreponame
|
|
|
|
elif (line.find("downloadspeedlimit|") != -1) and \
|
|
(not line.startswith("#")) and (split_line_len == 2):
|
|
|
|
try:
|
|
myval = int(split_line[1])
|
|
if myval > 0:
|
|
data['transfer_limit'] = myval
|
|
else:
|
|
data['transfer_limit'] = None
|
|
except (ValueError, IndexError,):
|
|
data['transfer_limit'] = None
|
|
|
|
elif (line.find("securityurl|") != -1) and \
|
|
(not line.startswith("#")) and (split_line_len == 2):
|
|
|
|
try:
|
|
data['security_advisories_url'] = split_line[1]
|
|
except (IndexError, ValueError, TypeError,):
|
|
continue
|
|
|
|
return data
|
|
|
|
def _clear_repository_cache(self, repoid = None):
|
|
"""
|
|
Internal method, go away!
|
|
"""
|
|
self.__cacher.discard()
|
|
self._clear_dump_cache(etpCache['world_available'])
|
|
self._clear_dump_cache(etpCache['world_update'])
|
|
self._clear_dump_cache(etpCache['check_package_update'])
|
|
self._clear_dump_cache(etpCache['filter_satisfied_deps'])
|
|
self._clear_dump_cache(etpCache['atomMatch'])
|
|
self._clear_dump_cache(etpCache['dep_tree'])
|
|
if repoid != None:
|
|
self._clear_dump_cache("%s/%s%s/" % (
|
|
etpCache['dbMatch'],etpConst['dbnamerepoprefix'],repoid,))
|
|
self._clear_dump_cache("%s/%s%s/" % (
|
|
etpCache['dbSearch'],etpConst['dbnamerepoprefix'],repoid,))
|
|
|
|
def _clear_dump_cache(self, dump_name, skip = []):
|
|
"""
|
|
Internal method, go away!
|
|
"""
|
|
dump_path = os.path.join(etpConst['dumpstoragedir'],dump_name)
|
|
dump_dir = os.path.dirname(dump_path)
|
|
#dump_file = os.path.basename(dump_path)
|
|
for currentdir, subdirs, files in os.walk(dump_dir):
|
|
path = os.path.join(dump_dir,currentdir)
|
|
if skip:
|
|
found = False
|
|
for myskip in skip:
|
|
if path.find(myskip) != -1:
|
|
found = True
|
|
break
|
|
if found: continue
|
|
for item in files:
|
|
if item.endswith(etpConst['cachedumpext']):
|
|
item = os.path.join(path,item)
|
|
try:
|
|
os.remove(item)
|
|
except (OSError, IOError,):
|
|
pass
|
|
try:
|
|
if not os.listdir(path):
|
|
os.rmdir(path)
|
|
except (OSError, IOError,):
|
|
pass
|
|
|
|
def __generic_parser(self, filepath):
|
|
"""
|
|
Internal method. This is the generic file parser here.
|
|
|
|
@param filepath valid path
|
|
@type filepath basestring
|
|
@return list parsed data
|
|
"""
|
|
return self.entropyTools.generic_file_content_parser(filepath)
|
|
|
|
def __remove_repo_cache(self, repoid = None):
|
|
"""
|
|
Internal method. Remove repository cache, because not valid anymore.
|
|
|
|
@param repoid repository identifier or None
|
|
@type repoid basestring or None
|
|
@return None
|
|
"""
|
|
if os.path.isdir(etpConst['dumpstoragedir']):
|
|
if repoid:
|
|
self._clear_repository_cache(repoid = repoid)
|
|
return
|
|
for repoid in self['repositories']['order']:
|
|
self._clear_repository_cache(repoid = repoid)
|
|
else:
|
|
try:
|
|
os.makedirs(etpConst['dumpstoragedir'])
|
|
except IOError, e:
|
|
if e.errno == 30: # readonly filesystem
|
|
etpUi['pretend'] = True
|
|
return
|
|
except OSError:
|
|
return
|
|
|
|
def __save_file_mtime(self, toread, tosaveinto):
|
|
"""
|
|
Internal method. Save mtime of a file to another file.
|
|
|
|
@param toread file path to read
|
|
@type toread basestring
|
|
@param tosaveinto path where to save retrieved mtime information
|
|
@type tosaveinto basestring
|
|
@return None
|
|
"""
|
|
if not os.path.isfile(toread):
|
|
currmtime = 0.0
|
|
else:
|
|
currmtime = os.path.getmtime(toread)
|
|
|
|
if not os.path.isdir(etpConst['dumpstoragedir']):
|
|
try:
|
|
os.makedirs(etpConst['dumpstoragedir'], 0775)
|
|
const_setup_perms(etpConst['dumpstoragedir'],
|
|
etpConst['entropygid'])
|
|
except IOError, e:
|
|
if e.errno == 30: # readonly filesystem
|
|
etpUi['pretend'] = True
|
|
return
|
|
except (OSError,), e:
|
|
# unable to create the storage directory
|
|
# useless to continue
|
|
return
|
|
|
|
try:
|
|
mtime_f = open(tosaveinto,"w")
|
|
except IOError, e: # unable to write?
|
|
if e.errno == 30: # readonly filesystem
|
|
etpUi['pretend'] = True
|
|
return
|
|
else:
|
|
mtime_f.write(str(currmtime))
|
|
mtime_f.flush()
|
|
mtime_f.close()
|
|
os.chmod(tosaveinto, 0664)
|
|
if etpConst['entropygid'] != None:
|
|
os.chown(tosaveinto, 0, etpConst['entropygid'])
|
|
|
|
|
|
def __validate_entropy_cache(self, maskfile, mtimefile, repoid = None):
|
|
"""
|
|
Internal method. Validates Entropy Cache
|
|
|
|
@param maskfile path of the setting file
|
|
@type maskfile basestring
|
|
@param mtimefile path where to save retrieved mtime information
|
|
@type mtimefile basestring
|
|
@param repoid repository identifier or None
|
|
@type repoid basestring or None
|
|
@return None
|
|
"""
|
|
|
|
# can't validate if running as user, moreover
|
|
# users can't make changes, so...
|
|
if os.getuid() != 0:
|
|
return
|
|
|
|
# handle on-disk cache validation
|
|
# in this case, repositories cache
|
|
# if file is changed, we must destroy cache
|
|
if not os.path.isfile(mtimefile):
|
|
# we can't know if it has been updated
|
|
# remove repositories caches
|
|
self.__remove_repo_cache(repoid = repoid)
|
|
self.__save_file_mtime(maskfile, mtimefile)
|
|
else:
|
|
# check mtime
|
|
try:
|
|
mtime_f = open(mtimefile,"r")
|
|
mtime = mtime_f.readline().strip()
|
|
mtime_f.close()
|
|
# compare with current mtime
|
|
try:
|
|
currmtime = str(os.path.getmtime(maskfile))
|
|
except OSError:
|
|
currmtime = "0.0"
|
|
if mtime != currmtime:
|
|
self.__remove_repo_cache(repoid = repoid)
|
|
self.__save_file_mtime(maskfile, mtimefile)
|
|
except (OSError, IOError,):
|
|
self.__remove_repo_cache(repoid = repoid)
|
|
self.__save_file_mtime(maskfile, mtimefile)
|