Files
entropy/libraries/entropy/const.py
Fabio Erculiani a7faac4d03 entropy.server: add support for expiration-based packages removal.
If enabled in server.conf, entropy server side interface can now
allow multiple packages in the same scope without forcefully being
'injected'.
Through packages.db.fatscope in database dir, you can choose what
packages are eligible to this status, you can use '*' wildcard to
enable the feature for all the packages.

Be warned, this feature is EXPERIMENTAL (read server.conf) and not
enabled by default.
2009-04-25 22:32:18 +02:00

1205 lines
42 KiB
Python

'''
# DESCRIPTION:
# Variables container
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 sys, os, stat
from entropy.i18n import _
# ETP_ARCH_CONST setup
ETP_ARCH_CONST = "x86"
if os.uname()[4] == "x86_64":
ETP_ARCH_CONST = "amd64"
etpSys = {
'archs': ["x86", "amd64"],
'api': '3',
'arch': ETP_ARCH_CONST,
'rootdir': "",
'maxthreads': 100,
'dirstoclean': set(),
'serverside': False,
'killpids': set()
}
etpUi = {
'debug': False,
'quiet': False,
'verbose': False,
'ask': False,
'pretend': False,
'mute': False,
'nolog': False,
'clean': False,
}
if "--debug" in sys.argv:
etpUi['debug'] = True
# static logging stuff
ETP_LOGLEVEL_NORMAL = 1
ETP_LOGLEVEL_VERBOSE = 2
ETP_LOGPRI_INFO = "[ INFO ]"
ETP_LOGPRI_WARNING = "[ WARNING ]"
ETP_LOGPRI_ERROR = "[ ERROR ]"
# disk caching dictionary
etpCache = {
# used to store information about files that
# should be merged using "equo conf merge"
'configfiles': 'conf/scanfs',
'dbMatch': 'match/db', # db atom match cache
'dbSearch': 'search/db', # db search cache
# used to store info about repository dependencies solving
'atomMatch': 'atom_match/atom_match_',
'install': 'resume/resume_install', # resume cache (install)
'remove': 'resume/resume_remove', # resume cache (remove)
'world': 'resume/resume_world', # resume cache (world)
'world_update': 'world_update/world_cache_',
'world_available': 'world_available/available_cache_',
'check_package_update': 'check_update/package_update_',
'advisories': 'security/advisories_cache_',
'dep_tree': 'deptree/dep_tree_',
'depends_tree': 'depends/depends_tree_',
'filter_satisfied_deps': 'depfilter/filter_satisfied_deps_',
'library_breakage': 'libs_break/library_breakage_',
'repolist': 'repos/repolist',
'repository_server': 'reposerver/item',
'eapi3_fetch': 'eapi3/segment_',
'ugc_votes': 'ugc/ugc_votes',
'ugc_downloads': 'ugc/ugc_downloads',
'ugc_docs': 'ugc/ugc_docs',
'ugc_srv_cache': 'ugc/ugc_srv_cache'
}
# kept for backward compatibility
# XXX will be removed before 10-10-2009
etpRepositories = {}
etpRepositoriesExcluded = {}
etpRepositoriesOrder = []
etpConst = {}
def initconfig_entropy_constants(rootdir):
"""
Main constants configurators, this is the only function that you should
call from the outside, everytime you want. it will reset all the variables
excluding those backed up previously.
@param rootdir current root directory, if any, or ""
@type rootdir str
@return None
"""
if rootdir and not os.path.isdir(rootdir):
raise AttributeError("FileNotFound: not a valid chroot.")
# save backed up settings
if etpConst.has_key('backed_up'):
backed_up_settings = etpConst.pop('backed_up')
else:
backed_up_settings = {}
const_default_settings(rootdir)
const_read_entropy_release()
const_create_working_dirs()
const_setup_entropy_pid()
const_configure_lock_paths()
# reflow back settings
etpConst.update(backed_up_settings)
etpConst['backed_up'] = backed_up_settings.copy()
if sys.excepthook == sys.__excepthook__:
sys.excepthook = const_handle_exception
def initConfig_entropyConstants(rootdir):
"""
@deprecated
Please use initconfig_entropy_constants
"""
import warnings
warnings.warn("deprecated please use initconfig_entropy_constants")
return initconfig_entropy_constants(rootdir)
def const_default_settings(rootdir):
"""
Initialization of all the Entropy base settings.
@param rootdir current root directory, if any, or ""
@type rootdir str
@return None
"""
default_etp_dir = rootdir+"/var/lib/entropy"
default_etp_tmpdir = "/tmp"
default_etp_repodir = "/packages/"+ETP_ARCH_CONST
default_etp_portdir = rootdir+"/usr/portage"
default_etp_distfilesdir = "/distfiles"
default_etp_dbdir = "/database/"+ETP_ARCH_CONST
default_etp_dbfile = "packages.db"
default_etp_dbclientfile = "equo.db"
default_etp_client_repodir = "/client"
default_etp_triggersdir = "/triggers/"+ETP_ARCH_CONST
default_etp_smartappsdir = "/smartapps/"+ETP_ARCH_CONST
default_etp_smartpackagesdir = "/smartpackages/"+ETP_ARCH_CONST
default_etp_cachesdir = "/caches/"
default_etp_securitydir = "/glsa/"
default_etp_setsdirname = "sets"
default_etp_setsdir = "/%s/" % (default_etp_setsdirname,)
default_etp_logdir = default_etp_dir+"/"+"logs"
default_etp_confdir = rootdir+"/etc/entropy"
default_etp_packagesdir = default_etp_confdir+"/packages"
default_etp_ugc_confdir = default_etp_confdir+"/ugc"
default_etp_syslogdir = rootdir+"/var/log/entropy/"
default_etp_vardir = rootdir+"/var/tmp/entropy"
edb_counter = rootdir+"/var/cache/edb/counter"
cmdline = []
cmdline_file = "/proc/cmdline"
if os.access(cmdline_file, os.R_OK) and os.path.isfile(cmdline_file):
with open(cmdline_file, "r") as cmdline_f:
cmdline = cmdline_f.readline().strip().split()
etpConst.clear()
my_const = {
'server_repositories': {},
'community': {
'mode': False,
},
'cmdline': cmdline,
'backed_up': {},
# entropy default installation directory
'installdir': '/usr/lib/entropy',
# etpConst['packagestmpdir'] --> temp directory
'packagestmpdir': default_etp_dir+default_etp_tmpdir,
# etpConst['packagesbindir'] --> repository
# where the packages will be stored
# by clients: to query if a package has been already downloaded
# by servers or rsync mirrors: to store already
# uploaded packages to the main rsync server
'packagesbindir': default_etp_dir+default_etp_repodir,
# etpConst['smartappsdir'] location where smart apps files are places
'smartappsdir': default_etp_dir+default_etp_smartappsdir,
# etpConst['smartpackagesdir'] location where
# smart packages files are places
'smartpackagesdir': default_etp_dir+default_etp_smartpackagesdir,
# etpConst['triggersdir'] location where external triggers are placed
'triggersdir': default_etp_dir+default_etp_triggersdir,
# directory where is stored our local portage tree
'portagetreedir': default_etp_portdir,
# directory where our sources are downloaded
'distfilesdir': default_etp_portdir+default_etp_distfilesdir,
# directory where entropy stores its configuration
'confdir': default_etp_confdir,
# same as above + /packages
'confpackagesdir': default_etp_packagesdir,
# system package sets dir
'confsetsdir': default_etp_packagesdir+default_etp_setsdir,
# just the dirname
'confsetsdirname': default_etp_setsdirname,
# entropy.conf file
'entropyconf': default_etp_confdir+"/entropy.conf",
# repositories.conf file
'repositoriesconf': default_etp_confdir+"/repositories.conf",
# server.conf file (generic server side settings)
'serverconf': default_etp_confdir+"/server.conf",
# client.conf file (generic entropy client side settings)
'clientconf': default_etp_confdir+"/client.conf",
# socket.conf file
'socketconf': default_etp_confdir+"/socket.conf",
# user by client interfaces
'packagesrelativepath': "packages/"+ETP_ARCH_CONST+"/",
'entropyworkdir': default_etp_dir, # Entropy workdir
# Entropy unpack directory
'entropyunpackdir': default_etp_vardir,
# Entropy packages image directory
'entropyimagerelativepath': "image",
# Gentoo xpak temp directory path
'entropyxpakrelativepath': "xpak",
# Gentoo xpak metadata directory path
'entropyxpakdatarelativepath': "data",
# Gentoo xpak metadata file name
'entropyxpakfilename': "metadata.xpak",
'etpdatabasetimestampfile': default_etp_dbfile+".timestamp",
'etpdatabaseconflictingtaggedfile': default_etp_dbfile + \
".conflicting_tagged",
# file containing a list of packages that are strictly
# required by the repository, thus forced
'etpdatabasesytemmaskfile': default_etp_dbfile+".system_mask",
'etpdatabasemaskfile': default_etp_dbfile+".mask",
'etpdatabaseupdatefile': default_etp_dbfile+".repo_updates",
'etpdatabaselicwhitelistfile': default_etp_dbfile+".lic_whitelist",
# the local/remote database revision file
'etpdatabaserevisionfile': default_etp_dbfile+".revision",
# missing dependencies black list file
'etpdatabasemissingdepsblfile': default_etp_dbfile + \
".missing_deps_blacklist",
# compressed file that contains all the "meta"
# files in a repository dir
'etpdatabasemetafilesfile': default_etp_dbfile+".meta",
# file that contains a list of the "meta"
# files not available in the repository
'etpdatabasemetafilesnotfound': default_etp_dbfile+".meta_notfound",
'etpdatabasehashfile': default_etp_dbfile+".md5", # its checksum
'etpdatabasedumphashfilebz2': default_etp_dbfile+".dump.bz2.md5",
'etpdatabasedumphashfilegzip': default_etp_dbfile+".dump.gz.md5",
# the remote database lock file
'etpdatabaselockfile': default_etp_dbfile+".lock",
# the remote database lock file
'etpdatabaseeapi3lockfile': default_etp_dbfile+".eapi3_lock",
# the remote database download lock file
'etpdatabasedownloadlockfile': default_etp_dbfile+".download.lock",
'etpdatabasecacertfile': "ca.cert",
'etpdatabaseservercertfile': "server.cert",
# when this file exists, the database is not synced
# anymore with the online one
'etpdatabasetaintfile': default_etp_dbfile+".tainted",
# Entropy sqlite database file default_etp_dir + \
# default_etp_dbdir+"/packages.db"
'etpdatabasefile': default_etp_dbfile,
# Entropy sqlite database file (gzipped)
'etpdatabasefilegzip': default_etp_dbfile+".gz",
# Entropy sqlite database file (bzipped2)
'etpdatabasefilebzip2': default_etp_dbfile+".bz2",
# Entropy sqlite database dump file (bzipped2)
'etpdatabasedumpbzip2': default_etp_dbfile+".dump.bz2",
# Entropy sqlite database dump file (gzipped)
'etpdatabasedumpgzip': default_etp_dbfile+".dump.gz",
# Entropy sqlite database dump file
'etpdatabasedump': default_etp_dbfile+".dump",
# expiration based server-side packages removal
'etpdatabaseexpbasedpkgsrm': default_etp_dbfile+".fatscope",
# Entropy default compressed database format
'etpdatabasefileformat': "bz2",
# Entropy compressed databases format support
'etpdatabasesupportedcformats': ["bz2", "gz"],
'etpdatabasecompressclasses': {
"bz2": ("bz2.BZ2File", "unpack_bzip2", "etpdatabasefilebzip2",
"etpdatabasedumpbzip2", "etpdatabasedumphashfilebz2"),
"gz": ("gzip.GzipFile", "unpack_gzip", "etpdatabasefilegzip",
"etpdatabasedumpgzip", "etpdatabasedumphashfilegzip")
},
# enable/disable packages RSS feed feature
'rss-feed': True,
# default name of the RSS feed
'rss-name': "packages.rss",
'rss-light-name': "updates.rss", # light version
# default URL to the entropy web interface
# (overridden in reagent.conf)
'rss-base-url': "http://packages.sabayonlinux.org/",
# default URL to the Operating System website
# (overridden in reagent.conf)
'rss-website-url': "http://www.sabayonlinux.org/",
# xml file where will be dumped ServerInterface.rssMessages dictionary
'rss-dump-name': "rss_database_actions",
'rss-max-entries': 10000, # maximum rss entries
'rss-light-max-entries': 300, # max entries for the light version
'rss-managing-editor': "lxnay@sabayonlinux.org", # updates submitter
# repository RSS-based notice board content
'rss-notice-board': "notice.rss",
'packagesetprefix': "@",
'userpackagesetsid': "__user__",
'setsconffilename': "sets.conf",
'cachedumpext': ".dmp",
'packagesext': ".tbz2",
# Extension of the file that contains the checksum
# of its releated package file
'packagesmd5fileext': ".md5",
'packagessha512fileext': ".sha512",
'packagessha256fileext': ".sha256",
'packagessha1fileext': ".sha1",
# Extension of the file that "contains" expiration mtime
'packagesexpirationfileext': ".expired",
# number of days after a package will be removed from mirrors
'packagesexpirationdays': 15,
# name of the trigger file that would be executed
# by equo inside triggerTools
'triggername': "trigger",
'trigger_sh_interpreter': "/usr/sbin/entropy.sh",
# proxy configuration constants, used system wide
'proxy': {
'ftp': None,
'http': None,
'username': None,
'password': None
},
# Entropy log level (default: 1 - see entropy.conf for more info)
'entropyloglevel': 1,
# Entropy Socket Interface log level
'socketloglevel': 2,
'spmloglevel': 1,
# Log dir where ebuilds store their stuff
'logdir': default_etp_logdir ,
'syslogdir': default_etp_syslogdir, # Entropy system tools log directory
'entropylogfile': default_etp_syslogdir+"entropy.log",
'equologfile': default_etp_syslogdir+"equo.log",
'spmlogfile': default_etp_syslogdir+"spm.log",
'socketlogfile': default_etp_syslogdir+"socket.log",
'etpdatabaseclientdir': default_etp_dir + default_etp_client_repodir + \
default_etp_dbdir,
# path to equo.db - client side database file
'etpdatabaseclientfilepath': default_etp_dir + \
default_etp_client_repodir + default_etp_dbdir + "/" + \
default_etp_dbclientfile,
# prefix of the name of self.dbname in
# entropy.db.LocalRepository class for the repositories
'dbnamerepoprefix': "repo_",
# prefix of database backups
'dbbackupprefix': 'etp_backup_',
# Entropy database API revision
'etpapi': etpSys['api'],
# contains the current running architecture
'currentarch': etpSys['arch'],
# Entropy supported Archs
'supportedarchs': etpSys['archs'],
# available branches, this only exists for the server part,
# these settings will be overridden by server.conf ones
'branches': [],
# default choosen branch (overridden by setting in repositories.conf)
'branch': "4",
# default allowed package keywords
'keywords': set([etpSys['arch'],"~"+etpSys['arch']]),
# Gentoo compatibility (/var/db/pkg + Portage availability)
'gentoo-compat': True,
# allow multiple packages in single scope server-side?
# this makes possible to have multiple versions of packages
# and handle the removal through expiration (using creation date)
'expiration_based_scope': False,
'edbcounter': edb_counter,
'libtest_blacklist': [],
'libtest_files_blacklist': [],
# our official repository name
'officialserverrepositoryid': "sabayonlinux.org",
# our official repository name
'officialrepositoryid': "sabayonlinux.org",
'conntestlink': "http://www.sabayonlinux.org",
# tag to append to .tbz2 file before entropy database (must be 32bytes)
'databasestarttag': "|ENTROPY:PROJECT:DB:MAGIC:START|",
'pidfile': default_etp_dir+"/entropy.pid",
'applicationlock': False,
# option to keep a backup of config files after
# being overwritten by equo conf update
'filesbackup': True,
# collision protection option, see client.conf for more info
'collisionprotect': 1,
# list of user specified CONFIG_PROTECT directories
# (see Gentoo manual to understand the meaining of this parameter)
'configprotect': [],
# list of user specified CONFIG_PROTECT_MASK directories
'configprotectmask': [],
# list of user specified configuration files that
# should be ignored and kept as they are
'configprotectskip': [],
# installed database CONFIG_PROTECT directories
'dbconfigprotect': [],
# installed database CONFIG_PROTECT_MASK directories
'dbconfigprotectmask': [],
# this will be used to show the number of updated
# files at the end of the processes
'configprotectcounter': 0,
# default Entropy release version
'entropyversion': "1.0",
# default system name (overidden by entropy.conf settings)
'systemname': "Sabayon Linux",
# Product identificator (standard, professional...)
'product': "standard",
'errorstatus': default_etp_confdir+"/code",
'systemroot': rootdir, # default system root
'uid': os.getuid(), # current running UID
'entropygid': None,
'sysgroup': "entropy",
'defaultumask': 022,
'storeumask': 002,
'gentle_nice': 15,
'current_nice': 0,
'default_nice': 0,
'server_treeupdatescalled': set(),
'client_treeupdatescalled': set(),
'spm': {
'(r)depend_id': 0,
'pdepend_id': 1,
'mdepend_id': 2, # actually, this is entropy-only
'ebuild_file_extension': "ebuild",
'preinst_phase': "preinst",
'postinst_phase': "postinst",
'prerm_phase': "prerm",
'postrm_phase': "postrm",
'setup_phase': "setup",
'compile_phase': "compile",
'install_phase': "install",
'unpack_phase': "unpack",
'ebuild_pkg_tag_var': "ENTROPY_PROJECT_TAG",
'global_make_conf': rootdir+"/etc/make.conf",
'global_package_keywords': rootdir+"/etc/portage/package.keywords",
'global_package_use': rootdir+"/etc/portage/package.use",
'global_package_mask': rootdir+"/etc/portage/package.mask",
'global_package_unmask': rootdir+"/etc/portage/package.unmask",
'global_make_profile': rootdir+"/etc/make.profile",
'global_make_profile_link_name' : "profile.link",
# source package manager executable
'exec': rootdir+"/usr/bin/emerge",
'env_update_cmd': rootdir+"/usr/sbin/env-update",
'source_profile': ["source", rootdir+"/etc/profile"],
'source_build_ext': ".ebuild",
'ask_cmd': "--ask",
'info_cmd': "--info",
'remove_cmd': "-C",
'nodeps_cmd': "--nodeps",
'fetchonly_cmd': "--fetchonly",
'buildonly_cmd': "--buildonly",
'oneshot_cmd': "--oneshot",
'pretend_cmd': "--pretend",
'verbose_cmd': "--verbose",
'nocolor_cmd': "--color=n",
'backend': "portage",
'available_backends': ["portage"],
'cache': {},
'xpak_entries': {
'description': "DESCRIPTION",
'homepage': "HOMEPAGE",
'chost': "CHOST",
'category': "CATEGORY",
'cflags': "CFLAGS",
'cxxflags': "CXXFLAGS",
'license': "LICENSE",
'src_uri': "SRC_URI",
'use': "USE",
'iuse': "IUSE",
'slot': "SLOT",
'provide': "PROVIDE",
'depend': "DEPEND",
'rdepend': "RDEPEND",
'pdepend': "PDEPEND",
'needed': "NEEDED",
'inherited': "INHERITED",
'keywords': "KEYWORDS",
'contents': "CONTENTS",
'counter': "COUNTER",
'defined_phases': "DEFINED_PHASES",
},
'system_packages': [],
'ignore-spm-downgrades': False,
},
# entropy client packages download speed limit (in kb/sec)
'downloadspeedlimit': None,
# data storage directory, useful to speed up
# entropy client across multiple issued commands
'dumpstoragedir': default_etp_dir+default_etp_cachesdir,
# where GLSAs are stored
'securitydir': default_etp_dir+default_etp_securitydir,
'securityurl': "http://community.sabayonlinux.org/security"
"/security-advisories.tar.bz2",
'safemodeerrors': {
'clientdb': 1,
},
'safemodereasons': {
0: _("All fine"),
1: _("Corrupted Client Repository. Please restore a backup."),
},
'misc_counters': {
'forced_atoms_update_ids': {
'__idtype__': 1,
'kde': 1,
},
},
'system_settings_plugins_ids': {
'client_plugin': "client_plugin",
'server_plugin': "server_plugin",
'server_plugin_fatscope': "server_plugin_fatscope",
},
'clientserverrepoid': "__system__",
'clientdbid': "client",
'serverdbid': "etpdb:",
'genericdbid': "generic",
'systemreleasefile': "/etc/sabayon-release",
# these are constants, for real settings
# look ad SystemSettings class
'socket_service': { # here are the constants
'hostname': "localhost",
'port': 1026,
'ssl_port': 1027, # above + 1
'timeout': 200,
'forked_requests_timeout': 300,
'max_command_length': 768000, # bytes
'threads': 5,
'session_ttl': 15,
'default_uid': 0,
'max_connections': 5,
'max_connections_per_host': 15,
'max_connections_per_host_barrier': 8,
'disabled_cmds': set(),
'ip_blacklist': set(),
'ssl_key': default_etp_confdir+"/socket_server.key",
'ssl_cert': default_etp_confdir+"/socket_server.crt",
'ssl_ca_cert': default_etp_confdir+"/socket_server.CA.crt",
'ssl_ca_pkey': default_etp_confdir+"/socket_server.CA.key",
'answers': {
'ok': chr(0)+"OK"+chr(0), # command run
'er': chr(0)+"ER"+chr(1), # execution error
'no': chr(0)+"NO"+chr(2), # not allowed
'cl': chr(0)+"CL"+chr(3), # close connection
'mcr': chr(0)+"MCR"+chr(4), # max connections reached
'eos': chr(0), # end of size,
'noop': chr(0)+"NOOP"+chr(0)
},
},
'install_sources': {
'unknown': 0,
'user': 1,
'automatic_dependency': 2,
},
'ugc_doctypes': {
'comments': 1,
'bbcode_doc': 2,
'image': 3,
'generic_file': 4,
'youtube_video': 5,
},
'ugc_doctypes_description': {
1: _('Comments'),
2: _('BBcode Documents'),
3: _('Images/Screenshots'),
4: _('Generic Files'),
5: _('YouTube(tm) Videos'),
},
'ugc_doctypes_description_singular': {
1: _('Comment'),
2: _('BBcode Document'),
3: _('Image/Screenshot'),
4: _('Generic File'),
5: _('YouTube(tm) Video'),
},
'ugc_accessfile': default_etp_ugc_confdir+"/access.xml",
'ugc_voterange': range(1, 6),
# handler settings
'handlers': {
# md5sum handler,
'md5sum': "md5sum.php?arch="+etpSys['arch']+"&package=",
# XXX: hardcoded?
'errorsend': "http://svn.sabayonlinux.org/entropy/standard"
"/sabayonlinux.org/handlers/http_error_report.php",
},
}
# set current nice level
try:
my_const['current_nice'] = os.nice(0)
except OSError:
pass
etpConst.update(my_const)
def const_set_nice_level(nice_level = 0):
"""
Change current process scheduler "nice" level.
@param nice_level new valid nice level
@type nice_level int
@return current_nice new nice level
"""
default_nice = etpConst['default_nice']
current_nice = etpConst['current_nice']
delta = current_nice - default_nice
try:
etpConst['current_nice'] = os.nice(delta*-1+nice_level)
except OSError:
pass
return current_nice
def const_extract_cli_repo_params(repostring, branch = None, product = None):
"""
Extract repository information from the provided repository string,
usually contained in the repository settings file, repositories.conf.
@param repostring basestring
@type repostring valid repository string
@return tuple composed by
reponame => repository identifier (string),
mydata => extracted repository information (dict)
"""
if branch == None:
branch = etpConst['branch']
if product == None:
product = etpConst['product']
reponame = repostring.split("|")[1].strip()
repodesc = repostring.split("|")[2].strip()
repopackages = repostring.split("|")[3].strip()
repodatabase = repostring.split("|")[4].strip()
eapi3_port = int(etpConst['socket_service']['port'])
eapi3_ssl_port = int(etpConst['socket_service']['ssl_port'])
eapi3_formatcolon = repodatabase.rfind("#")
if eapi3_formatcolon != -1:
try:
ports = repodatabase[eapi3_formatcolon+1:].split(",")
eapi3_port = int(ports[0])
if len(ports) > 1:
eapi3_ssl_port = int(ports[1])
repodatabase = repodatabase[:eapi3_formatcolon]
except (ValueError, IndexError,):
eapi3_port = int(etpConst['socket_service']['port'])
eapi3_ssl_port = int(etpConst['socket_service']['ssl_port'])
dbformat = etpConst['etpdatabasefileformat']
dbformatcolon = repodatabase.rfind("#")
if dbformatcolon != -1:
if dbformat in etpConst['etpdatabasesupportedcformats']:
try:
dbformat = repodatabase[dbformatcolon+1:]
except (IndexError, ValueError, TypeError,):
pass
repodatabase = repodatabase[:dbformatcolon]
mydata = {}
mydata['repoid'] = reponame
mydata['service_port'] = eapi3_port
mydata['ssl_service_port'] = eapi3_ssl_port
mydata['description'] = repodesc
mydata['packages'] = []
mydata['plain_packages'] = []
mydata['dbpath'] = etpConst['etpdatabaseclientdir'] + "/" + reponame + \
"/" + product + "/" + etpConst['currentarch'] + "/" + branch
mydata['dbcformat'] = dbformat
if not dbformat in etpConst['etpdatabasesupportedcformats']:
mydata['dbcformat'] = etpConst['etpdatabasesupportedcformats'][0]
mydata['plain_database'] = repodatabase
mydata['database'] = repodatabase + "/" + product + "/" + \
reponame + "/database/" + etpConst['currentarch'] + \
"/" + branch
mydata['notice_board'] = mydata['database'] + "/" + \
etpConst['rss-notice-board']
mydata['local_notice_board'] = mydata['dbpath'] + "/" + \
etpConst['rss-notice-board']
mydata['dbrevision'] = "0"
dbrevision_file = os.path.join(mydata['dbpath'],
etpConst['etpdatabaserevisionfile'])
if os.path.isfile(dbrevision_file) and os.access(dbrevision_file, os.R_OK):
with open(dbrevision_file, "r") as dbrev_f:
mydata['dbrevision'] = dbrev_f.readline().strip()
# initialize CONFIG_PROTECT
# will be filled the first time the db will be opened
mydata['configprotect'] = None
mydata['configprotectmask'] = None
repopackages = [x.strip() for x in repopackages.split() if x.strip()]
repopackages = [x for x in repopackages if (x.startswith('http://') or \
x.startswith('ftp://') or x.startswith('file://'))]
for repo_package in repopackages:
try:
repo_package = str(repo_package)
except (UnicodeDecodeError,UnicodeEncodeError,):
continue
mydata['plain_packages'].append(repo_package)
mydata['packages'].append(repo_package + "/" + product + "/" + reponame)
return reponame, mydata
def const_read_entropy_release():
"""
Read Entropy release file content and fill etpConst['entropyversion']
@return None
"""
# handle Entropy Version
revision_file = "../libraries/revision"
if not os.path.isfile(revision_file):
revision_file = os.path.join(etpConst['installdir'],
'libraries/revision')
if os.path.isfile(revision_file) and \
os.access(revision_file,os.R_OK):
with open(revision_file, "r") as rev_f:
myrev = rev_f.readline().strip()
etpConst['entropyversion'] = myrev
def const_setup_entropy_pid(just_read = False):
"""
Setup Entropy pid file, if possible and if UID = 0 (root).
If the application is run with --no-pid-handling argument,
this function will have no effect. If just_read is specified,
this function will only try to read the current pid string in
the Entropy pid file (etpConst['pidfile']). If any other entropy
istance is currently owning the contained pid, etpConst['applicationlock']
becomes True.
@param just_read only read the current pid file, if any and if possible
@type just_read bool
@return None
"""
if ("--no-pid-handling" in sys.argv) and (not just_read):
return
# PID creation
pid = os.getpid()
pid_file = etpConst['pidfile']
if os.path.isfile(pid_file) and os.access(pid_file, os.R_OK):
try:
with open(pid_file,"r") as pid_f:
found_pid = str(pid_f.readline().strip())
except (IOError, OSError, UnicodeEncodeError, UnicodeDecodeError,):
found_pid = "0000" # which is always invalid
if found_pid != str(pid):
# is found_pid still running ?
pid_path = "%s/proc/%s" % (etpConst['systemroot'], found_pid,)
if os.path.isdir(pid_path) and found_pid:
etpConst['applicationlock'] = True
elif not just_read:
# if root, write new pid
#if etpConst['uid'] == 0:
if os.access(pid_file, os.W_OK):
try:
with open(pid_file,"w") as pid_f:
pid_f.write(str(pid))
pid_f.flush()
except IOError, err:
if err.errno == 30: # readonly filesystem
pass
else:
raise
try:
const_chmod_entropy_pid()
except OSError:
pass
elif not just_read:
#if etpConst['uid'] == 0:
if os.access(os.path.dirname(pid_file), os.W_OK):
if os.path.exists(pid_file):
if os.path.islink(pid_file):
os.remove(pid_file)
elif os.path.isdir(pid_file):
import shutil
shutil.rmtree(pid_file)
with open(pid_file,"w") as pid_fw:
pid_fw.write(str(pid))
pid_fw.flush()
try:
const_chmod_entropy_pid()
except OSError:
pass
def const_secure_config_file(config_file):
"""
Setup entropy file needing strict permissions, no world readable.
@param config_file valid config file path
@type config_file basestring
@return None
"""
try:
mygid = const_get_entropy_gid()
except KeyError:
mygid = 0
try:
const_setup_file(config_file, mygid, 0660)
except (OSError, IOError,):
pass
def const_chmod_entropy_pid():
"""
Setup entropy pid file permissions, if possible.
@return None
"""
try:
mygid = const_get_entropy_gid()
except KeyError:
mygid = 0
const_setup_file(etpConst['pidfile'], mygid, 0664)
def const_create_working_dirs():
"""
Setup Entropy directory structure, as much automagically as possible.
@return None
"""
# handle pid file
piddir = os.path.dirname(etpConst['pidfile'])
if not os.path.exists(piddir) and (etpConst['uid'] == 0):
os.makedirs(piddir)
# create tmp dir
#if not os.path.isdir(xpakpath_dir):
# os.makedirs(xpakpath_dir,0775)
# const_setup_file(xpakpath_dir,
# create user if it doesn't exist
gid = None
try:
gid = const_get_entropy_gid()
except KeyError:
if etpConst['uid'] == 0:
# create group
# avoid checking cause it's not mandatory for entropy/equo itself
const_add_entropy_group()
try:
gid = const_get_entropy_gid()
except KeyError:
pass
# Create paths
keys = [x for x in etpConst if isinstance(etpConst[x],basestring)]
for key in keys:
if not etpConst[key] or \
etpConst[key].endswith(".conf") or \
not os.path.isabs(etpConst[key]) or \
etpConst[key].endswith(".cfg") or \
etpConst[key].endswith(".tmp") or \
etpConst[key].find(".db") != -1 or \
etpConst[key].find(".log") != -1 or \
os.path.isdir(etpConst[key]) or \
not key.endswith("dir"):
continue
# allow users to create dirs in custom paths,
# so don't fail here even if we don't have permissions
try:
key_dir = etpConst[key]
d_paths = []
while not os.path.isdir(key_dir):
d_paths.append(key_dir)
key_dir = os.path.dirname(key_dir)
d_paths = sorted(d_paths)
for d_path in d_paths:
os.mkdir(d_path)
const_setup_file(d_path, gid, 0775)
except (OSError, IOError,):
pass
if gid:
etpConst['entropygid'] = gid
if not os.path.isdir(etpConst['entropyworkdir']):
try:
os.makedirs(etpConst['entropyworkdir'])
except OSError:
pass
w_gid = os.stat(etpConst['entropyworkdir'])[stat.ST_GID]
if w_gid != gid:
const_setup_perms(etpConst['entropyworkdir'], gid)
if not os.path.isdir(etpConst['entropyunpackdir']):
try:
os.makedirs(etpConst['entropyunpackdir'])
except OSError:
pass
try:
w_gid = os.stat(etpConst['entropyunpackdir'])[stat.ST_GID]
if w_gid != gid:
if os.path.isdir(etpConst['entropyunpackdir']):
const_setup_perms(etpConst['entropyunpackdir'], gid)
except OSError:
pass
# always setup /var/lib/entropy/client permissions
if not const_islive():
# aufs/unionfs will start to leak otherwise
const_setup_perms(etpConst['etpdatabaseclientdir'], gid)
def const_configure_lock_paths():
"""
Setup Entropy lock file paths.
@return None
"""
etpConst['locks'] = {
'using_resources': os.path.join(etpConst['etpdatabaseclientdir'],
'.using_resources'),
}
def const_extract_srv_repo_params(repostring, product = None):
"""
Analyze a server repository string (usually contained in server.conf),
extracting all the parameters.
@param repostring repository string
@type repostring basestring
@return None
"""
if product == None:
product = etpConst['product']
mydata = {}
repoid = repostring.split("|")[1].strip()
repodesc = repostring.split("|")[2].strip()
repouris = repostring.split("|")[3].strip()
repohandlers = repostring.split("|")[4].strip()
service_url = None
eapi3_port = int(etpConst['socket_service']['port'])
eapi3_ssl_port = int(etpConst['socket_service']['ssl_port'])
if len(repostring.split("|")) > 5:
service_url = repostring.split("|")[5].strip()
eapi3_formatcolon = service_url.rfind("#")
if eapi3_formatcolon != -1:
try:
ports = service_url[eapi3_formatcolon+1:].split(",")
eapi3_port = int(ports[0])
if len(ports) > 1:
eapi3_ssl_port = int(ports[1])
service_url = service_url[:eapi3_formatcolon]
except (ValueError, IndexError,):
eapi3_port = int(etpConst['socket_service']['port'])
eapi3_ssl_port = int(etpConst['socket_service']['ssl_port'])
mydata = {}
mydata['repoid'] = repoid
mydata['description'] = repodesc
mydata['mirrors'] = []
mydata['community'] = False
mydata['service_url'] = service_url
mydata['service_port'] = eapi3_port
mydata['ssl_service_port'] = eapi3_ssl_port
if repohandlers:
repohandlers = os.path.join(repohandlers, product, repoid, "handlers")
mydata['handler'] = repohandlers
uris = repouris.split()
for uri in uris:
mydata['mirrors'].append(uri)
return repoid, mydata
def const_setup_perms(mydir, gid):
"""
Setup permissions and group id (GID) to a directory, recursively.
@param mydir valid file path
@type mydir basestring
@param gid valid group id (GID)
@type gid int
@return None
"""
if gid == None:
return
for currentdir, subdirs, files in os.walk(mydir):
try:
cur_gid = os.stat(currentdir)[stat.ST_GID]
if cur_gid != gid:
os.chown(currentdir, -1, gid)
cur_mod = const_get_chmod(currentdir)
if cur_mod != oct(0775):
os.chmod(currentdir, 0775)
except OSError:
pass
for item in files:
item = os.path.join(currentdir, item)
try:
const_setup_file(item, gid, 0664)
except OSError:
pass
def const_setup_file(myfile, gid, chmod):
"""
Setup file permissions and group id (GID).
@param myfile valid file path
@type myfile basestring
@param gid valid group id (GID)
@type gid int
@param chmod permissions
@type chmod integer representing an octal
@return None
"""
cur_gid = os.stat(myfile)[stat.ST_GID]
if cur_gid != gid:
os.chown(myfile, -1, gid)
const_set_chmod(myfile, chmod)
# you need to convert to int
def const_get_chmod(myfile):
"""
This function get the current permissions of the specified
file. If you want to use the returning value with const_set_chmod
you need to convert it back to int.
@param myfile valid file path
@type myfile basestring
@return octal representing permissions
"""
myst = os.stat(myfile)[stat.ST_MODE]
return oct(myst & 0777)
def const_set_chmod(myfile, chmod):
"""
This function sets specified permissions to a file.
If they differ from the current ones.
@param myfile valid file path
@type myfile basestring
@param chmod permissions
@type chmod integer representing an octal
@return None
"""
cur_mod = const_get_chmod(myfile)
if cur_mod != oct(chmod):
os.chmod(myfile, chmod)
def const_get_entropy_gid():
"""
This function tries to retrieve the "entropy" user group
GID.
@return None or KeyError exception
"""
group_file = etpConst['systemroot']+'/etc/group'
if not os.path.isfile(group_file):
raise KeyError
with open(group_file,"r") as group_f:
for line in group_f.readlines():
if line.startswith('%s:' % (etpConst['sysgroup'],)):
try:
gid = int(line.split(":")[2])
except ValueError:
raise KeyError
return gid
raise KeyError
def const_add_entropy_group():
"""
This function looks for an "entropy" user group.
If not available, it tries to create one.
@return None
"""
group_file = etpConst['systemroot']+'/etc/group'
if not os.path.isfile(group_file):
raise KeyError
ids = set()
with open(group_file,"r") as group_f:
for line in group_f.readlines():
if line and line.split(":"):
try:
myid = int(line.split(":")[2])
except ValueError:
pass
ids.add(myid)
if ids:
# starting from 1000, get the first free
new_id = 1000
while 1:
new_id += 1
if new_id not in ids:
break
else:
new_id = 10000
with open(group_file,"aw") as group_fw:
group_fw.seek(0, 2)
app_line = "entropy:x:%s:\n" % (new_id,)
group_fw.write(app_line)
group_fw.flush()
def const_islive():
"""
Live environments (Operating System running off a CD/DVD)
must feature the "cdroot" parameter in kernel /proc/cmdline
@return bool stating if we are running Live or not
"""
if "cdroot" in etpConst['cmdline']:
return True
return False
def const_kill_threads():
"""
Entropy threads killer. Even if Python threads cannot
be stopped or killed, TimeScheduled ones can, exporting
the kill() method.
@return None
"""
import threading
threads = threading.enumerate()
for running_t in threads:
if not hasattr(running_t,'kill'):
continue
running_t.kill()
running_t.join()
def const_handle_exception(etype, value, t_back):
"""
Our default Python exception handler. It kills
all the threads generated by Entropy before
raising exceptions. Overloads sys.excepthook
@param etype exception type
@param value exception value
@param t_back traceback object?
@return sys.__excepthook__
"""
try:
const_kill_threads()
except ImportError:
pass
return sys.__excepthook__(etype, value, t_back)
# load config
initconfig_entropy_constants(etpSys['rootdir'])