Files
entropy/libraries/entropy/client/misc.py
T
2009-10-05 19:54:08 +02:00

285 lines
11 KiB
Python

# -*- coding: utf-8 -*-
"""
@author: Fabio Erculiani <lxnay@sabayonlinux.org>
@contact: lxnay@sabayonlinux.org
@copyright: Fabio Erculiani
@license: GPL-2
B{Entropy Package Manager Client Miscellaneous Interface}.
"""
import os
import sys
import shutil
import subprocess
from entropy.client.interfaces import Client
from entropy.exceptions import *
from entropy.const import etpConst, etpCache, const_convert_to_rawstring
from entropy.output import darkred, darkgreen, red, brown, blue
from entropy.tools import getstatusoutput
from entropy.i18n import _
class FileUpdates:
def __init__(self, EquoInstance):
if not isinstance(EquoInstance, Client):
mytxt = _("A valid Client instance or subclass is needed")
raise IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
self.Entropy = EquoInstance
from entropy.cache import EntropyCacher
from entropy.core.settings.base import SystemSettings
self.Cacher = EntropyCacher()
self.SystemSettings = SystemSettings()
self.scandata = None
def merge_file(self, key):
self.scanfs(dcache = True)
self.do_backup(key)
source_file = etpConst['systemroot'] + self.scandata[key]['source']
dest_file = etpConst['systemroot'] + self.scandata[key]['destination']
if os.access(source_file, os.R_OK):
shutil.move(source_file, dest_file)
self.remove_from_cache(key)
def remove_file(self, key):
self.scanfs(dcache = True)
source_file = etpConst['systemroot'] + self.scandata[key]['source']
if os.path.isfile(source_file) and os.access(source_file, os.W_OK):
os.remove(source_file)
self.remove_from_cache(key)
def do_backup(self, key):
self.scanfs(dcache = True)
sys_set_plg_id = \
etpConst['system_settings_plugins_ids']['client_plugin']
files_backup = self.Entropy.SystemSettings[sys_set_plg_id]['misc']['filesbackup']
dest_file = etpConst['systemroot'] + self.scandata[key]['destination']
if files_backup and os.path.isfile(dest_file):
bcount = 0
backupfile = etpConst['systemroot'] + \
os.path.dirname(self.scandata[key]['destination']) + \
"/._entropy_backup." + str(bcount) + "_" + \
os.path.basename(self.scandata[key]['destination'])
while os.path.lexists(backupfile):
bcount += 1
backupfile = etpConst['systemroot'] + \
os.path.dirname(self.scandata[key]['destination']) + \
"/._entropy_backup." + str(bcount) + "_" + \
os.path.basename(self.scandata[key]['destination'])
try:
shutil.copy2(dest_file, backupfile)
except IOError:
pass
def scanfs(self, dcache = True, quiet = False):
if dcache:
if self.scandata != None:
return self.scandata
# can we load cache?
try:
z = self.load_cache()
if z != None:
self.scandata = z
return self.scandata
except (CacheCorruptionError, KeyError, IOError, OSError,):
pass
scandata = {}
counter = 0
name_cache = set()
client_conf_protect = self.Entropy.get_system_config_protect()
for path in client_conf_protect:
# this avoids encoding issues hands down
try:
path = path.encode('utf-8')
except (UnicodeEncodeError,):
path = path.encode(sys.getfilesystemencoding())
# it's a file?
scanfile = False
if os.path.isfile(path):
# find inside basename
path = os.path.dirname(path)
scanfile = True
for currentdir, subdirs, files in os.walk(path):
for item in files:
if scanfile:
if path != item:
continue
filepath = os.path.join(currentdir, item)
# FIXME: with Python 3.x we can remove const_convert...
# and not use path.encode('utf-8')
if item.startswith(const_convert_to_rawstring("._cfg")):
# further check then
number = item[5:9]
try:
int(number)
except ValueError:
continue # not a valid etc-update file
if item[9] != "_": # no valid format provided
continue
if filepath in name_cache:
continue # skip, already done
name_cache.add(filepath)
mydict = self.generate_dict(filepath)
if mydict['automerge']:
if not quiet:
mytxt = _("Automerging file")
self.Entropy.updateProgress(
darkred("%s: %s") % (
mytxt,
darkgreen(etpConst['systemroot'] + mydict['source']),
),
importance = 0,
type = "info"
)
if os.path.isfile(etpConst['systemroot']+mydict['source']):
try:
os.rename(etpConst['systemroot']+mydict['source'],
etpConst['systemroot']+mydict['destination'])
except (OSError, IOError,) as e:
if not quiet:
mytxt = "%s :: %s: %s. %s: %s" % (
red(_("System Error")),
red(_("Cannot automerge file")),
brown(etpConst['systemroot'] + mydict['source']),
blue("error"),
e,
)
self.Entropy.updateProgress(
mytxt,
importance = 1,
type = "warning"
)
continue
else:
counter += 1
scandata[counter] = mydict.copy()
if not quiet:
try:
self.Entropy.updateProgress(
"("+blue(str(counter))+") " + red(" file: ") + \
os.path.dirname(filepath) + "/" + os.path.basename(filepath)[10:],
importance = 1,
type = "info"
)
except:
pass # possible encoding issues
# store data
self.Cacher.push(etpCache['configfiles'], scandata)
self.scandata = scandata.copy()
return scandata
def load_cache(self):
sd = self.Cacher.pop(etpCache['configfiles'])
if not isinstance(sd, dict):
raise CacheCorruptionError("CacheCorruptionError")
# quick test if data is reliable
try:
name_cache = set()
for x in sd:
mysource = sd[x]['source']
# filter dupies
if mysource in name_cache:
sd.pop(x)
continue
if not os.path.isfile(etpConst['systemroot']+mysource):
raise CacheCorruptionError("CacheCorruptionError")
name_cache.add(mysource)
return sd
except (KeyError, EOFError, IOError,):
raise CacheCorruptionError("CacheCorruptionError")
def add_to_cache(self, filepath, quiet = False):
self.scanfs(dcache = True, quiet = quiet)
keys = list(self.scandata.keys())
try:
for key in keys:
if self.scandata[key]['source'] == filepath[len(etpConst['systemroot']):]:
del self.scandata[key]
except:
pass
# get next counter
if keys:
keys = sorted(keys)
index = keys[-1]
else:
index = 0
index += 1
mydata = self.generate_dict(filepath)
self.scandata[index] = mydata.copy()
self.Cacher.push(etpCache['configfiles'], self.scandata)
def remove_from_cache(self, key):
self.scanfs(dcache = True)
try:
del self.scandata[key]
except:
pass
self.Cacher.push(etpCache['configfiles'], self.scandata)
return self.scandata
def generate_dict(self, filepath):
item = os.path.basename(filepath)
currentdir = os.path.dirname(filepath)
tofile = item[10:]
number = item[5:9]
try:
int(number)
except:
mytxt = _("Invalid config file number")
raise InvalidDataType("InvalidDataType: %s '0000->9999'." % (mytxt,))
tofilepath = currentdir+"/"+tofile
mydict = {}
mydict['revision'] = number
mydict['destination'] = tofilepath[len(etpConst['systemroot']):]
mydict['source'] = filepath[len(etpConst['systemroot']):]
mydict['automerge'] = False
if not os.path.isfile(tofilepath):
mydict['automerge'] = True
if (not mydict['automerge']):
# is it trivial?
try:
if not os.path.lexists(filepath): # if file does not even exist
return mydict
if os.path.islink(filepath):
# if it's broken, skip diff and automerge
if not os.path.exists(filepath):
return mydict
result = getstatusoutput('diff -Nua "%s" "%s" | grep "^[+-][^+-]" | grep -v \'# .Header:.*\'' % (filepath, tofilepath,))[1]
if not result:
mydict['automerge'] = True
except:
pass
# another test
if (not mydict['automerge']):
try:
if not os.path.lexists(filepath): # if file does not even exist
return mydict
if os.path.islink(filepath):
# if it's broken, skip diff and automerge
if not os.path.exists(filepath):
return mydict
result = subprocess.call('diff -Bbua "%s" "%s" | egrep \'^[+-]\' | egrep -v \'^[+-][\t ]*#|^--- |^\+\+\+ \' | egrep -qv \'^[-+][\t ]*$\'' % (filepath, tofilepath,), shell = True)
if result == 1:
mydict['automerge'] = True
except:
pass
return mydict