|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
# Copyright 2008-2016 Mir Calculate. http://www.calculate-linux.org
|
|
|
#
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
# You may obtain a copy of the License at
|
|
|
#
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
#
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
# See the License for the specific language governing permissions and
|
|
|
# limitations under the License.
|
|
|
import hashlib
|
|
|
import select
|
|
|
import random
|
|
|
import string
|
|
|
|
|
|
import os
|
|
|
from os import path
|
|
|
import subprocess
|
|
|
from subprocess import Popen, PIPE, STDOUT
|
|
|
import stat
|
|
|
from shutil import copytree, rmtree, copy2
|
|
|
import re
|
|
|
import sys
|
|
|
from itertools import *
|
|
|
import tarfile
|
|
|
import fcntl
|
|
|
from tools import SingletonParam
|
|
|
|
|
|
from contextlib import contextmanager
|
|
|
import signal
|
|
|
|
|
|
_ = lambda x: x
|
|
|
from calculate.lib.cl_lang import setLocalTranslate
|
|
|
|
|
|
setLocalTranslate('cl_lib3', sys.modules[__name__])
|
|
|
|
|
|
|
|
|
@contextmanager
|
|
|
def timeout(seconds):
|
|
|
def timeout_handler(signum, frame):
|
|
|
pass
|
|
|
|
|
|
original_handler = signal.signal(signal.SIGALRM, timeout_handler)
|
|
|
|
|
|
try:
|
|
|
signal.alarm(seconds)
|
|
|
yield
|
|
|
finally:
|
|
|
signal.alarm(0)
|
|
|
signal.signal(signal.SIGALRM, original_handler)
|
|
|
|
|
|
|
|
|
try:
|
|
|
from magic import (open as type_file, MAGIC_NONE, MAGIC_CONTINUE,
|
|
|
MAGIC_MIME_TYPE, MAGIC_COMPRESS,
|
|
|
MAGIC_MIME_ENCODING, MAGIC_SYMLINK)
|
|
|
except ImportError as e:
|
|
|
try:
|
|
|
from magic import (open as type_file, NONE as MAGIC_NONE,
|
|
|
CONTINUE as MAGIC_CONTNUE,
|
|
|
MIME_TYPE as MAGIC_MIME_TYPE,
|
|
|
COMPRESS as MAGIC_COMPRESS,
|
|
|
MIME_ENCODING as MIME_ENCODING,
|
|
|
SYMLINK as MAGIC_SYMLINK)
|
|
|
except (ImportError, AttributeError):
|
|
|
type_file = None
|
|
|
MAGIC_NONE = 0
|
|
|
MAGIC_SYMLINK = 0x002
|
|
|
MAGIC_COMPRESS = 0x004
|
|
|
MAGIC_CONTINUE = 0x020
|
|
|
MAGIC_MIME_TYPE = 0x010
|
|
|
MAGIC_MIME_ENCODING = 0x400
|
|
|
|
|
|
from calculate.lib.cl_lang import setLocalTranslate
|
|
|
|
|
|
setLocalTranslate('cl_lib3', sys.modules[__name__])
|
|
|
|
|
|
reSearch = lambda pattern, listar: map(lambda x: x.groups(),
|
|
|
filter(None,
|
|
|
map(pattern.search, listar)))
|
|
|
|
|
|
|
|
|
class FilesError(Exception):
|
|
|
pass
|
|
|
|
|
|
|
|
|
class StdoutableProcess(object):
|
|
|
def __init__(self):
|
|
|
self.pipe = None
|
|
|
|
|
|
def get_stdout(self):
|
|
|
return PIPE
|
|
|
|
|
|
def close(self):
|
|
|
pass
|
|
|
|
|
|
|
|
|
class KeyboardInputProcess(StdoutableProcess):
|
|
|
def get_stdout(self):
|
|
|
return None
|
|
|
|
|
|
|
|
|
class PipeProcess(StdoutableProcess):
|
|
|
def get_stdout(self):
|
|
|
return PIPE
|
|
|
|
|
|
|
|
|
class process(StdoutableProcess):
|
|
|
"""Execute system command by Popen
|
|
|
|
|
|
Examples:
|
|
|
|
|
|
execute program and get result:
|
|
|
if process("/bin/gzip","/boot/somefile").success():
|
|
|
print "Gzip success"
|
|
|
|
|
|
unzip and process unzip data by cpio (list files):
|
|
|
processGzip = process("/bin/gzip","-dc","/boot/initrd")
|
|
|
processCpio = process("/bin/cpio","-tf",stdin=processGzip)
|
|
|
filelist = processCpio.readlines()
|
|
|
|
|
|
execute command and send data:
|
|
|
processGrub = process("/sbin/grub")
|
|
|
processGrub.write("root (hd0,0)\n")
|
|
|
processGrub.write("setup (hd0)\n")
|
|
|
processGrub.write("quit\n")
|
|
|
isok = processGrub.success()
|
|
|
|
|
|
union stdout and stderr:
|
|
|
process("/bin/ls","/",stderr=STDOUT)
|
|
|
|
|
|
result to stdout:
|
|
|
process("/bin/ls",stdout=None)
|
|
|
|
|
|
get data from keyboard:
|
|
|
process("/bin/cat",stdin=None)
|
|
|
"""
|
|
|
STDOUT = STDOUT
|
|
|
PIPE = PIPE
|
|
|
|
|
|
def __init__(self, command, *params, **kwarg):
|
|
|
super(process, self).__init__()
|
|
|
if "stdin" not in kwarg:
|
|
|
stdin = PipeProcess()
|
|
|
else:
|
|
|
if kwarg["stdin"] is None:
|
|
|
stdin = KeyboardInputProcess()
|
|
|
else:
|
|
|
stdin = kwarg["stdin"]
|
|
|
self.stdout = kwarg.get("stdout", PIPE)
|
|
|
self.envdict = kwarg.get("envdict", os.environ.copy())
|
|
|
self.envdict["LANG"] = kwarg.get('lang', 'C')
|
|
|
self.langc = "langc" in kwarg
|
|
|
|
|
|
self.stderr = kwarg.get("stderr", PIPE)
|
|
|
self.cwd = kwarg.get("cwd", None)
|
|
|
self.command = getProgPath(command)
|
|
|
if not self.command:
|
|
|
raise FilesError(_("Command not found '%s'") %
|
|
|
command)
|
|
|
self.command = [self.command] + list(params)
|
|
|
self.stdin = stdin
|
|
|
self.iter = None
|
|
|
self.cacheresult = None
|
|
|
self.cacheerr = None
|
|
|
self.communicated = False
|
|
|
self._cachedata = []
|
|
|
|
|
|
def _open(self):
|
|
|
"""Open pipe if it not open"""
|
|
|
if not self.pipe:
|
|
|
self.pipe = Popen(self.command,
|
|
|
stdout=self.stdout,
|
|
|
stdin=self.stdin.get_stdout(),
|
|
|
stderr=self.stderr,
|
|
|
cwd=self.cwd,
|
|
|
close_fds=True,
|
|
|
env=self.envdict)
|
|
|
|
|
|
def get_stdout(self):
|
|
|
"""Get current stdout"""
|
|
|
self.close()
|
|
|
self._open()
|
|
|
self.cacheresult = ""
|
|
|
return self.pipe.stdout
|
|
|
|
|
|
def write(self, data):
|
|
|
"""Write to process stdin"""
|
|
|
self._open()
|
|
|
self.pipe.stdin.write(data)
|
|
|
self.pipe.stdin.flush()
|
|
|
|
|
|
def close(self):
|
|
|
"""Close stdin"""
|
|
|
if self.pipe:
|
|
|
self.pipe.poll()
|
|
|
if self.pipe.stdin:
|
|
|
self.pipe.stdin.close()
|
|
|
if self.stdin:
|
|
|
self.stdin.close()
|
|
|
|
|
|
def readerr(self):
|
|
|
self.read()
|
|
|
if self.cacheerr is None:
|
|
|
try:
|
|
|
self.cacheerr = self.pipe.stderr.read()
|
|
|
except IOError:
|
|
|
self.cacheerr = ""
|
|
|
return self.cacheerr
|
|
|
|
|
|
def readByLine(self):
|
|
|
_cacheerr = []
|
|
|
try:
|
|
|
self._open()
|
|
|
if self.cacheresult is None:
|
|
|
_stdout = self.pipe.stdout.fileno()
|
|
|
_stderr = self.pipe.stderr.fileno()
|
|
|
reads = [_stdout, _stderr]
|
|
|
while True:
|
|
|
ret = select.select(reads, [], [])
|
|
|
for fd in ret[0]:
|
|
|
if fd == _stdout:
|
|
|
s = self.pipe.stdout.readline()
|
|
|
yield s
|
|
|
self._cachedata.append(s)
|
|
|
if fd == _stderr:
|
|
|
s = self.pipe.stderr.readline()
|
|
|
_cacheerr.append(s)
|
|
|
if self.pipe.poll() is not None:
|
|
|
break
|
|
|
except KeyboardInterrupt:
|
|
|
self.kill()
|
|
|
raise KeyboardInterrupt
|
|
|
finally:
|
|
|
if self.cacheresult is None:
|
|
|
self.cacheresult = "\n".join(self._cachedata)
|
|
|
self.cacheerr = "".join(_cacheerr)
|
|
|
self.close()
|
|
|
|
|
|
def read(self):
|
|
|
"""Read all data"""
|
|
|
try:
|
|
|
self._open()
|
|
|
if self.cacheresult is None:
|
|
|
self.cacheresult, self.cacheerr = self.pipe.communicate()
|
|
|
return self.cacheresult
|
|
|
except KeyboardInterrupt:
|
|
|
self.kill()
|
|
|
raise KeyboardInterrupt
|
|
|
finally:
|
|
|
self.close()
|
|
|
|
|
|
def readlines(self):
|
|
|
"""Read lines"""
|
|
|
return self.read().split('\n')
|
|
|
|
|
|
def __iter__(self):
|
|
|
"""Get iterator"""
|
|
|
if not self.iter:
|
|
|
self.iter = iter(self.readlines())
|
|
|
return self.iter
|
|
|
|
|
|
def kill(self):
|
|
|
"""Kill this process"""
|
|
|
if self.pipe:
|
|
|
self.pipe.kill()
|
|
|
|
|
|
def next(self):
|
|
|
"""Next string from stdout"""
|
|
|
return self.__iter__().next()
|
|
|
|
|
|
def returncode(self):
|
|
|
"""Get return code"""
|
|
|
self.read()
|
|
|
return self.pipe.returncode
|
|
|
|
|
|
def success(self):
|
|
|
"""Success or not"""
|
|
|
return self.returncode() == 0
|
|
|
|
|
|
def failed(self):
|
|
|
"""Failed or not"""
|
|
|
return self.returncode() != 0
|
|
|
|
|
|
|
|
|
def getModeFile(nameFile, mode="all"):
|
|
|
"""Выдает информацию о файле
|
|
|
mode=="all"
|
|
|
права файла, владелец, группа файла
|
|
|
mode=="mode"
|
|
|
права файла
|
|
|
mode=="owner"
|
|
|
владелец, группа файла
|
|
|
"""
|
|
|
fst = os.lstat(nameFile)
|
|
|
if mode == "all":
|
|
|
return stat.S_IMODE(fst.st_mode), fst.st_uid, fst.st_gid
|
|
|
if mode == "mode":
|
|
|
return stat.S_IMODE(fst.st_mode)
|
|
|
if mode == "owner":
|
|
|
return fst.st_uid, fst.st_gid
|
|
|
|
|
|
|
|
|
def chown(fn, uid=None, gid=None):
|
|
|
"""
|
|
|
Поменять uid и gid файла если текущие отличаются
|
|
|
:param fn: файл
|
|
|
:param uid: UID или None
|
|
|
:param gid: GID или None
|
|
|
"""
|
|
|
f_stat = os.stat(fn)
|
|
|
if uid is None:
|
|
|
uid = f_stat.st_uid
|
|
|
if gid is None:
|
|
|
gid = f_stat.st_gid
|
|
|
if uid != f_stat.st_uid or gid != f_stat.st_gid:
|
|
|
os.lchown(fn, uid, gid)
|
|
|
|
|
|
|
|
|
class FilePermission(object):
|
|
|
"""
|
|
|
Права на файл
|
|
|
"""
|
|
|
UserWrite = 0200
|
|
|
UserRead = 0400
|
|
|
UserExecute = 0100
|
|
|
UserAll = UserWrite | UserRead | UserExecute
|
|
|
|
|
|
GroupWrite = 020
|
|
|
GroupRead = 040
|
|
|
GroupExecute = 010
|
|
|
GroupAll = GroupWrite | GroupRead | GroupExecute
|
|
|
|
|
|
OtherWrite = 02
|
|
|
OtherRead = 04
|
|
|
OtherExecute = 01
|
|
|
OtherAll = OtherWrite | OtherRead | OtherExecute
|
|
|
|
|
|
SetUid = 04000
|
|
|
SetGid = 02000
|
|
|
StickyBit = 01000
|
|
|
|
|
|
|
|
|
def chmod(fn, mode):
|
|
|
"""
|
|
|
Поменять права на файл если текущие права отличаются
|
|
|
:param fn: файл
|
|
|
:param mode: права (0755)
|
|
|
"""
|
|
|
f_stat = os.stat(fn)
|
|
|
if f_stat.st_mode != mode:
|
|
|
os.chmod(fn, mode)
|
|
|
|
|
|
|
|
|
def chownR(directory, uid, gid):
|
|
|
"""Recusive chown"""
|
|
|
|
|
|
def chownPaths(rootPath, listPath, uid, gid):
|
|
|
for chPath in listPath:
|
|
|
chownPath = path.join(rootPath, chPath)
|
|
|
statInfo = os.lstat(chownPath)[stat.ST_MODE]
|
|
|
if stat.S_ISLNK(statInfo):
|
|
|
os.lchown(chownPath, uid, gid)
|
|
|
else:
|
|
|
os.chown(chownPath, uid, gid)
|
|
|
|
|
|
for root, dirs, files in os.walk(directory):
|
|
|
# меняем владельца директории
|
|
|
os.chown(root, uid, gid)
|
|
|
# Меняем владельца директорий
|
|
|
chownPaths(root, dirs, uid, gid)
|
|
|
# Меняем владельца файлов
|
|
|
chownPaths(root, files, uid, gid)
|
|
|
return True
|
|
|
|
|
|
|
|
|
class proxy_type_file:
|
|
|
def __init__(self, flags):
|
|
|
self.flags = flags
|
|
|
|
|
|
def load(self):
|
|
|
pass
|
|
|
|
|
|
def setflags(self, flags):
|
|
|
self.flags = flags
|
|
|
|
|
|
def close(self):
|
|
|
pass
|
|
|
|
|
|
def _get_cmd_by_flags(self, flags):
|
|
|
"""
|
|
|
Получить команду для выполнения по флагам
|
|
|
"""
|
|
|
paramdict = {MAGIC_CONTINUE: "-k",
|
|
|
MAGIC_SYMLINK: "-L",
|
|
|
MAGIC_MIME_TYPE: "--mime-type",
|
|
|
MAGIC_MIME_ENCODING: "--mime-encoding",
|
|
|
MAGIC_COMPRESS: "-z"}
|
|
|
appendParam = map(lambda x: paramdict[x],
|
|
|
filter(lambda x: flags & x,
|
|
|
sorted(paramdict.keys())))
|
|
|
fileCmd = getProgPath('/usr/bin/file')
|
|
|
return [fileCmd, '-b'] + appendParam
|
|
|
|
|
|
def file(self, filename):
|
|
|
if path.exists(filename):
|
|
|
processFile = process(
|
|
|
*(self._get_cmd_by_flags(self.flags) + [filename]))
|
|
|
if processFile.success():
|
|
|
return processFile.read().rstrip()
|
|
|
return None
|
|
|
|
|
|
|
|
|
class typeFile(object):
|
|
|
"""Получение типа файла"""
|
|
|
|
|
|
def __init__(self, magic=MAGIC_MIME_ENCODING | MAGIC_MIME_TYPE):
|
|
|
if type_file is None:
|
|
|
self.magicObject = proxy_type_file(MAGIC_NONE)
|
|
|
else:
|
|
|
self.magicObject = type_file(MAGIC_NONE)
|
|
|
self.magicObject.load()
|
|
|
self.magicObject.setflags(magic)
|
|
|
|
|
|
def __del__(self):
|
|
|
"""Закрываем magic"""
|
|
|
self.magicObject.close()
|
|
|
|
|
|
def getMType(self, filename):
|
|
|
"""Информация о типе файла"""
|
|
|
try:
|
|
|
ret = self.magicObject.file(filename)
|
|
|
except UnicodeDecodeError:
|
|
|
try:
|
|
|
ret = self.magicObject.file(filename.decode('utf-8'))
|
|
|
except UnicodeDecodeError:
|
|
|
return None
|
|
|
# fix for kernel 3.7.7 (bad work samba)
|
|
|
if ret is None and self.magicObject.errno() == 5:
|
|
|
r, w = os.pipe()
|
|
|
devnull = os.open(os.devnull, os.O_WRONLY)
|
|
|
cat = subprocess.Popen(['/bin/cat', filename], stdout=w,
|
|
|
stderr=devnull, close_fds=True)
|
|
|
ret = self.magicObject.descriptor(r)
|
|
|
os.close(w)
|
|
|
os.close(devnull)
|
|
|
cat.wait()
|
|
|
return ret
|
|
|
|
|
|
def isBinary(self, filename):
|
|
|
"""является ли файл бинарным"""
|
|
|
mime = self.getMType(filename)
|
|
|
if mime:
|
|
|
if mime.startswith("text"):
|
|
|
return False
|
|
|
else:
|
|
|
return True
|
|
|
return None
|
|
|
|
|
|
|
|
|
class processProgress(process):
|
|
|
"""
|
|
|
Generator like process progress
|
|
|
"""
|
|
|
|
|
|
def __init__(self, command, *params, **kwarg):
|
|
|
process.__init__(self, command, *params, **kwarg)
|
|
|
self.readsize = kwarg.get("readsize", 10)
|
|
|
self.delimeter = re.compile("\n")
|
|
|
self.init(**kwarg)
|
|
|
self._cachedata = []
|
|
|
self.buf = ""
|
|
|
|
|
|
def init(self, *args, **kwarg):
|
|
|
pass
|
|
|
|
|
|
def processInit(self):
|
|
|
pass
|
|
|
|
|
|
def processEnd(self):
|
|
|
pass
|
|
|
|
|
|
def processString(self, strdata):
|
|
|
self._cachedata.append(strdata)
|
|
|
return strdata
|
|
|
|
|
|
def processBuffer(self, buf):
|
|
|
return ""
|
|
|
|
|
|
def progress(self):
|
|
|
try:
|
|
|
res = self.processInit()
|
|
|
if res is not None:
|
|
|
yield res
|
|
|
self._open()
|
|
|
if self.cacheresult is None:
|
|
|
self.buf = ""
|
|
|
fd = self.pipe.stdout.fileno()
|
|
|
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
|
|
|
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
|
|
|
hasData = True
|
|
|
while hasData or self.pipe.poll() is None:
|
|
|
hasData = False
|
|
|
try:
|
|
|
part = self.pipe.stdout.read(self.readsize)
|
|
|
if not part:
|
|
|
continue
|
|
|
except IOError as er:
|
|
|
# re-raise not "Resource temporary unavailable"
|
|
|
if er.errno != 11:
|
|
|
raise
|
|
|
continue
|
|
|
hasData = True
|
|
|
if self.buf:
|
|
|
self.buf += part
|
|
|
else:
|
|
|
self.buf = part
|
|
|
res = self.processBuffer(part)
|
|
|
if res:
|
|
|
yield res
|
|
|
while self.delimeter.search(self.buf):
|
|
|
strdata, self.buf = self.delimeter.split(self.buf, 1)
|
|
|
res = self.processString(strdata)
|
|
|
if res:
|
|
|
yield res
|
|
|
res = self.processEnd()
|
|
|
if res is not None:
|
|
|
yield res
|
|
|
except KeyboardInterrupt:
|
|
|
self.pipe.kill()
|
|
|
raise
|
|
|
finally:
|
|
|
if self._cachedata:
|
|
|
self.cacheresult = "\n".join(self._cachedata)
|
|
|
else:
|
|
|
self.cacheresult = ""
|
|
|
|
|
|
|
|
|
def countFiles(dirpath, onefilesystem=True):
|
|
|
"""
|
|
|
Count files in specified dirpath
|
|
|
"""
|
|
|
num = 1
|
|
|
for dirpath, dirnames, filenames in os.walk(dirpath):
|
|
|
num += len(set(dirnames) | set(filenames))
|
|
|
if onefilesystem:
|
|
|
mountDirs = filter(lambda x: path.ismount(path.join(dirpath, x)),
|
|
|
dirnames)
|
|
|
for dirname in mountDirs:
|
|
|
dirnames.remove(dirname)
|
|
|
return num
|
|
|
|
|
|
|
|
|
def getFilesCount(directory):
|
|
|
"""Alias for compatibility"""
|
|
|
return countFiles(directory, onefilesystem=False)
|
|
|
|
|
|
|
|
|
def copyDir(srcDir, destDir):
|
|
|
"""Копируем директорию srcDir в destDir
|
|
|
|
|
|
При копировании сохраняются владелец, группа, права
|
|
|
"""
|
|
|
|
|
|
def ignoreFile(pathname, names):
|
|
|
"""Игнорирование сокетов при копировании"""
|
|
|
ignore = []
|
|
|
for name in names:
|
|
|
if stat.S_ISSOCK(os.lstat(path.join(pathname, name))[stat.ST_MODE]):
|
|
|
ignore.append(name)
|
|
|
return ignore
|
|
|
|
|
|
copytree(srcDir, destDir, ignore=ignoreFile)
|
|
|
return True
|
|
|
|
|
|
|
|
|
def removeDir(rmDir):
|
|
|
"""Рекурсивное удаление директории"""
|
|
|
rmtree(rmDir)
|
|
|
return True
|
|
|
|
|
|
|
|
|
def check_rw(dn):
|
|
|
"""
|
|
|
Проверить досупена ли указанная директория на запись
|
|
|
:param dn:
|
|
|
:return:
|
|
|
"""
|
|
|
|
|
|
def _random_string(l=10):
|
|
|
return "".join(
|
|
|
random.choice(string.ascii_letters) for i in xrange(0, l))
|
|
|
|
|
|
mark_file = None
|
|
|
while mark_file is None or path.exists(mark_file):
|
|
|
mark_file = path.join(dn, "_mark_%s" % _random_string())
|
|
|
try:
|
|
|
open(mark_file, 'w')
|
|
|
os.unlink(mark_file)
|
|
|
return True
|
|
|
except (OSError, IOError):
|
|
|
return False
|
|
|
|
|
|
|
|
|
def removeFileWithEmptyDirectory(rmName, stopDirectory="/"):
|
|
|
"""
|
|
|
Удалить файл и все пустые родительские директории
|
|
|
:param rmName: путь до файла или директории
|
|
|
"""
|
|
|
if rmName != stopDirectory and path.exists(rmName):
|
|
|
if path.isdir(rmName):
|
|
|
if not listDirectory(rmName, fullPath=True):
|
|
|
os.rmdir(rmName)
|
|
|
removeFileWithEmptyDirectory(path.dirname(rmName),
|
|
|
stopDirectory=stopDirectory)
|
|
|
else:
|
|
|
os.unlink(rmName)
|
|
|
removeFileWithEmptyDirectory(path.dirname(rmName),
|
|
|
stopDirectory=stopDirectory)
|
|
|
|
|
|
|
|
|
def getRunCommands(not_chroot=False, chroot=None):
|
|
|
"""List run program"""
|
|
|
|
|
|
def getCmd(procNum):
|
|
|
cmdLineFile = '/proc/%s/cmdline' % procNum
|
|
|
try:
|
|
|
if path.exists(cmdLineFile):
|
|
|
if not_chroot:
|
|
|
root_link = '/proc/%s/root' % procNum
|
|
|
if os.readlink(root_link) != "/":
|
|
|
return ""
|
|
|
if chroot is not None:
|
|
|
root_link = '/proc/%s/root' % procNum
|
|
|
if os.readlink(root_link) != chroot:
|
|
|
return ""
|
|
|
return open(cmdLineFile, 'r').read().strip()
|
|
|
except Exception:
|
|
|
pass
|
|
|
return ""
|
|
|
|
|
|
if not os.access('/proc', os.R_OK):
|
|
|
return []
|
|
|
return filter(None,
|
|
|
map(getCmd,
|
|
|
filter(lambda x: x.isdigit(),
|
|
|
listDirectory('/proc'))))
|
|
|
|
|
|
|
|
|
def isMount(dn):
|
|
|
"""
|
|
|
Возвращает путь примонтированного dn
|
|
|
"""
|
|
|
|
|
|
def find_names(old_dn):
|
|
|
dn = path.abspath(old_dn)
|
|
|
yield dn
|
|
|
if dn.startswith('/dev'):
|
|
|
info = device.getUdevDeviceInfo(name=dn)
|
|
|
if 'DM_VG_NAME' in info and 'DM_LV_NAME' in info:
|
|
|
yield '/dev/mapper/{vg}-{lv}'.format(vg=info['DM_VG_NAME'],
|
|
|
lv=info['DM_LV_NAME'])
|
|
|
|
|
|
def get_overlay_mounts(line):
|
|
|
mounts = line.split(' ')
|
|
|
yield mounts[1]
|
|
|
for dn in re.findall("(?:lowerdir=|upperdir=|workdir=)([^,]+)",
|
|
|
mounts[3]):
|
|
|
yield dn
|
|
|
|
|
|
find_data = set(find_names(dn))
|
|
|
ret = ""
|
|
|
for line in readLinesFile('/etc/mtab'):
|
|
|
if " overlay " not in line:
|
|
|
if " " in line:
|
|
|
mounts = set(line.split(' ')[:2])
|
|
|
if mounts & find_data:
|
|
|
ret = (mounts - find_data).pop()
|
|
|
else:
|
|
|
mounts = set(get_overlay_mounts(line))
|
|
|
dest = line.split(' ')[1]
|
|
|
if mounts & find_data:
|
|
|
if dest in find_data:
|
|
|
ret = "overlay"
|
|
|
else:
|
|
|
return dest
|
|
|
return ret
|
|
|
|
|
|
|
|
|
def commonPath(*paths):
|
|
|
"""Return common path from list of paths"""
|
|
|
paths = map(lambda x: path.normpath(x).split('/'), paths)
|
|
|
res = map(lambda x: x[0],
|
|
|
filter(lambda x: filter(lambda y: x[0] == y, x[1:]), zip(*paths)))
|
|
|
return "/".join(res)
|
|
|
|
|
|
|
|
|
def childMounts(pathname):
|
|
|
"""Get all mount points which contain path"""
|
|
|
if pathname != "none":
|
|
|
absPath = path.abspath(pathname)
|
|
|
else:
|
|
|
absPath = pathname
|
|
|
mtabFile = '/etc/mtab'
|
|
|
if not os.access(mtabFile, os.R_OK):
|
|
|
return ""
|
|
|
return reduce(lambda x, y: x + [y],
|
|
|
filter(lambda x: commonPath(absPath, x[0]) == absPath or \
|
|
|
commonPath(absPath, x[1]) == absPath,
|
|
|
map(lambda x: [x[0], x[1]],
|
|
|
map(lambda x: x.split(" "),
|
|
|
open(mtabFile)))),
|
|
|
[])
|
|
|
|
|
|
|
|
|
def pathJoin(*paths):
|
|
|
"""Складывает пути, в отличии от os.path.join, складывает абсолютные пути"""
|
|
|
if len(paths) == 1:
|
|
|
return paths[0]
|
|
|
return reduce(path.join,
|
|
|
filter(lambda x: x and x != "/",
|
|
|
map(lambda x: x.startswith("/") and x[1:] or x,
|
|
|
paths[1:])), paths[0])
|
|
|
|
|
|
|
|
|
def checkDigestFile(digestfile):
|
|
|
"""Check digest by digestfile"""
|
|
|
reEntry = re.compile(r"# (\S+) HASH\n(\S+) (\S+)", re.S)
|
|
|
result = []
|
|
|
for alg, hashdata, filename in reEntry.findall(
|
|
|
open(digestfile, 'r').read()):
|
|
|
if hasattr(hashlib, alg.lower()):
|
|
|
hashobj = getattr(hashlib, alg.lower())
|
|
|
filename = path.join(path.dirname(digestfile), filename)
|
|
|
if os.path.exists(filename):
|
|
|
digest = hashobj(open(filename, 'r').read())
|
|
|
result.append((alg,
|
|
|
digest.hexdigest().upper() == hashdata.upper()))
|
|
|
return result
|
|
|
|
|
|
|
|
|
def listDirectory(directory, fullPath=False, onlyDir=False):
|
|
|
"""Get files from directory, if it exists"""
|
|
|
if not path.exists(directory):
|
|
|
return []
|
|
|
try:
|
|
|
if fullPath:
|
|
|
if onlyDir:
|
|
|
return filter(lambda x: path.isdir(x),
|
|
|
map(lambda x: path.join(directory, x),
|
|
|
os.listdir(directory)))
|
|
|
else:
|
|
|
return map(lambda x: path.join(directory, x),
|
|
|
os.listdir(directory))
|
|
|
else:
|
|
|
if onlyDir:
|
|
|
return filter(lambda x: path.isdir(path.join(directory, x)),
|
|
|
os.listdir(directory))
|
|
|
else:
|
|
|
return os.listdir(directory)
|
|
|
except OSError:
|
|
|
pass
|
|
|
return []
|
|
|
|
|
|
|
|
|
def makeDirectory(pathname):
|
|
|
"""Make directory and parent.
|
|
|
|
|
|
Return False on error"""
|
|
|
try:
|
|
|
parent = path.split(path.normpath(pathname))[0]
|
|
|
if not path.exists(parent):
|
|
|
makeDirectory(parent)
|
|
|
else:
|
|
|
if path.exists(pathname):
|
|
|
return True
|
|
|
os.mkdir(pathname)
|
|
|
return True
|
|
|
except (OSError, IOError):
|
|
|
return False
|
|
|
|
|
|
|
|
|
def readLinesFile(filename):
|
|
|
"""Read file by line"""
|
|
|
try:
|
|
|
if path.exists(filename):
|
|
|
for line in open(filename, 'r'):
|
|
|
yield line.rstrip('\n')
|
|
|
except (OSError, IOError):
|
|
|
pass
|
|
|
finally:
|
|
|
raise StopIteration
|
|
|
|
|
|
|
|
|
def readFile(filename, tailbyte=None):
|
|
|
"""
|
|
|
Прочитать целый файл или вернуть пустую строку.
|
|
|
tailbyte: прочитать только последнее указанное количество байт
|
|
|
"""
|
|
|
try:
|
|
|
if path.exists(filename):
|
|
|
with open(filename, 'r') as f:
|
|
|
if tailbyte:
|
|
|
seeksize = max(0, os.stat(filename).st_size - tailbyte)
|
|
|
if seeksize:
|
|
|
f.seek(seeksize)
|
|
|
return f.read()
|
|
|
except (OSError, IOError):
|
|
|
pass
|
|
|
return ""
|
|
|
|
|
|
|
|
|
def writeFile(filename):
|
|
|
"""
|
|
|
Открыть файл на запись и создать необходимые каталоги
|
|
|
"""
|
|
|
dn = path.dirname(filename)
|
|
|
if not path.exists(dn):
|
|
|
os.makedirs(dn)
|
|
|
return open(filename, 'w')
|
|
|
|
|
|
|
|
|
class GetProgPath(object):
|
|
|
"""Get full path of program or False"""
|
|
|
cache = {}
|
|
|
|
|
|
def __call__(self, progname, prefix="/"):
|
|
|
baseprogname = path.basename(progname)
|
|
|
key = (baseprogname, prefix)
|
|
|
if key in self.cache:
|
|
|
return self.cache[key]
|
|
|
bindir = ('/bin', '/sbin', '/usr/sbin', '/usr/bin')
|
|
|
if progname.startswith('/'):
|
|
|
if path.exists(pathJoin(prefix, progname)):
|
|
|
self.cache[key] = progname
|
|
|
return progname
|
|
|
for progname in (pathJoin(x, baseprogname) for x in bindir):
|
|
|
if path.exists(pathJoin(prefix, progname)):
|
|
|
self.cache[key] = progname
|
|
|
return progname
|
|
|
return False
|
|
|
# env = {"LANG":"C"}
|
|
|
# env.update(os.environ.items() + [("PATH",common.getpathenv())] +\
|
|
|
# env.items())
|
|
|
# res = runOsCommand("which %s"%baseprogname,env_dict=env)
|
|
|
# if path.isabs(progname) and path.exists(progname):
|
|
|
# self.cache[baseprogname] = progname
|
|
|
# return self.cache[baseprogname]
|
|
|
# elif res[0] == 0:
|
|
|
# self.cache[baseprogname] = res[1][0].strip()
|
|
|
# return self.cache[baseprogname]
|
|
|
# else:
|
|
|
# return False
|
|
|
|
|
|
|
|
|
getProgPath = GetProgPath()
|
|
|
|
|
|
|
|
|
def checkUtils(*utils):
|
|
|
"""Check utils, exit if it not found and return fullpath"""
|
|
|
retval = []
|
|
|
for util in utils:
|
|
|
utilPath = getProgPath(util)
|
|
|
if not utilPath:
|
|
|
raise FilesError(_("Command not found '%s'") %
|
|
|
path.basename(util))
|
|
|
retval.append(utilPath)
|
|
|
if len(retval) == 1:
|
|
|
return retval[0]
|
|
|
else:
|
|
|
return retval
|
|
|
|
|
|
|
|
|
def getCmdLineParam(paramName):
|
|
|
"""Get value of param /proc/cmdline. If param not found then empty.
|
|
|
"""
|
|
|
cmdLine = '/proc/cmdline'
|
|
|
paramName = "%s=" % paramName
|
|
|
params = \
|
|
|
map(lambda x: x.partition('=')[2],
|
|
|
filter(lambda x: x.startswith(paramName),
|
|
|
readFile(cmdLine).split(' ')))
|
|
|
if params:
|
|
|
return params[-1]
|
|
|
else:
|
|
|
return ""
|
|
|
|
|
|
|
|
|
from device import getUUIDDict
|
|
|
import device
|
|
|
|
|
|
|
|
|
class MountHelper(object):
|
|
|
"""Data reader for fstab"""
|
|
|
data_file = '/etc/fstab'
|
|
|
NAME, DIR, TYPE, OPTS, FREQ, PASSNO = range(0, 6)
|
|
|
|
|
|
def __init__(self, data_file=None, devs=()):
|
|
|
if data_file:
|
|
|
self.data_file = data_file
|
|
|
self.cache = []
|
|
|
self.rotateCache = []
|
|
|
self.dictUUID = getUUIDDict(devs=devs)
|
|
|
self.rebuildCache()
|
|
|
|
|
|
def rebuildCache(self):
|
|
|
"""Rebuild cache from fstab file"""
|
|
|
|
|
|
def setlen(ar):
|
|
|
return ar[:6] + [""] * (6 - len(ar))
|
|
|
|
|
|
self.cache = \
|
|
|
map(lambda x: setlen(map(lambda y: y.strip(), x.split())),
|
|
|
filter(lambda x: x.strip() and not x.lstrip().startswith("#"),
|
|
|
open(self.data_file, 'r').read().split('\n')))
|
|
|
for data in self.cache:
|
|
|
convertDev = lambda x: path.realpath(x) if x.startswith('/') else x
|
|
|
data[0] = device.getUdevDeviceInfo(
|
|
|
name=convertDev(self.dictUUID.get(data[0], data[0]))
|
|
|
).get('DEVNAME', data[0])
|
|
|
data[1] = data[1] if data[2] != "swap" else "swap"
|
|
|
self.rotateCache = zip(*self.cache)
|
|
|
|
|
|
def getBy(self, what=DIR, where=NAME, _in=None, eq=None,
|
|
|
contains=None, noteq=None, allentry=False):
|
|
|
"""Get data from fstab"""
|
|
|
if eq is not None:
|
|
|
filterfunc = lambda x: x[where] == eq
|
|
|
elif _in is not None:
|
|
|
filterfunc = lambda x: x[where] in _in
|
|
|
elif contains is not None:
|
|
|
filterfunc = lambda x: contains in x[where]
|
|
|
else:
|
|
|
filterfunc = lambda x: x[where] != noteq
|
|
|
res = map(lambda x: x[what], filter(filterfunc, self.cache))
|
|
|
if allentry:
|
|
|
return res
|
|
|
else:
|
|
|
return "" if not res else res[-1]
|
|
|
|
|
|
def getFields(self, *fields):
|
|
|
"""Get all data by specifie fields"""
|
|
|
return zip(*reduce(lambda x, y: x + [self.rotateCache[y]], fields, []))
|
|
|
|
|
|
def isExists(self, what=DIR, eq=None, noteq=None):
|
|
|
"""Field with condition exist in fstab"""
|
|
|
if not eq is None:
|
|
|
filterfunc = lambda x: x[what] == eq
|
|
|
else:
|
|
|
filterfunc = lambda x: x[what] != noteq
|
|
|
return bool(filter(filterfunc, self.cache))
|
|
|
|
|
|
|
|
|
class FStab(MountHelper):
|
|
|
"""Data reader for fstab"""
|
|
|
__metaclass__ = SingletonParam
|
|
|
data_file = '/etc/fstab'
|
|
|
|
|
|
|
|
|
class Mounts(MountHelper):
|
|
|
"""Data reader for fstab"""
|
|
|
data_file = '/etc/mtab'
|
|
|
|
|
|
|
|
|
def tarLinks(rootpath, archpath, skip=()):
|
|
|
"""
|
|
|
Поместить все симлинки в архив
|
|
|
"""
|
|
|
links = []
|
|
|
lenprefix = len(path.normpath(rootpath)) + 1
|
|
|
if path.exists(archpath):
|
|
|
os.unlink(archpath)
|
|
|
# create arch
|
|
|
tar = tarfile.open(archpath, "w:bz2")
|
|
|
# find links
|
|
|
if skip:
|
|
|
reSkip = re.compile("|".join(map(lambda x: x.replace("*", ".*"),
|
|
|
skip))).search
|
|
|
else:
|
|
|
reSkip = lambda x: False
|
|
|
for link in ifilterfalse(reSkip,
|
|
|
find(rootpath, filetype=FindFileType.SymbolicLink,
|
|
|
onefilesystem=True)):
|
|
|
ti = tar.gettarinfo(link)
|
|
|
ti.name = link[lenprefix:]
|
|
|
tar.addfile(ti)
|
|
|
links.append(link)
|
|
|
tar.close()
|
|
|
return links
|
|
|
|
|
|
|
|
|
def getch():
|
|
|
"""
|
|
|
Get char from keyboard
|
|
|
"""
|
|
|
import sys, tty, termios
|
|
|
|
|
|
try:
|
|
|
fd = sys.stdin.fileno()
|
|
|
old_settings = termios.tcgetattr(fd)
|
|
|
try:
|
|
|
tty.setraw(sys.stdin.fileno())
|
|
|
ch = sys.stdin.read(1)
|
|
|
finally:
|
|
|
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
|
|
return ch
|
|
|
except Exception:
|
|
|
return ""
|
|
|
|
|
|
|
|
|
class FindFileType(object):
|
|
|
"""
|
|
|
Типы файлов для find
|
|
|
"""
|
|
|
RegularFile = "f"
|
|
|
Directory = "d"
|
|
|
SymbolicLink = "l"
|
|
|
Block = "b"
|
|
|
Character = "c"
|
|
|
NamedPipe = "p"
|
|
|
|
|
|
|
|
|
def find(directory, onefilesystem=False, filetype=None,
|
|
|
fullpath=True, depth=None, downfilter=None):
|
|
|
"""
|
|
|
Поиск файлов в каталоге
|
|
|
:param directory: каталог в котором производится поиск
|
|
|
:param onefilesystem: искать только в текущей файловой системе
|
|
|
:param filetype: тип файла (FindFileType)
|
|
|
:param fullpath: выдавать полный путь
|
|
|
:param depth: глубина поиска
|
|
|
:param downfilter: фильтр поиска
|
|
|
:return: генератор списка файлов
|
|
|
"""
|
|
|
directory = path.normpath(directory)
|
|
|
ldirectory = len(directory)
|
|
|
if downfilter or onefilesystem or depth:
|
|
|
_downfilter = lambda dp: (
|
|
|
(depth is None or dp[ldirectory:].count('/') < depth) and
|
|
|
(not downfilter or downfilter(dp)) and
|
|
|
(not onefilesystem or not path.ismount(dp)))
|
|
|
else:
|
|
|
_downfilter = None
|
|
|
|
|
|
if filetype:
|
|
|
def dirfilter(dp):
|
|
|
link = path.islink(dp)
|
|
|
return ((FindFileType.SymbolicLink in filetype and link)
|
|
|
or (FindFileType.Directory in filetype and not link))
|
|
|
|
|
|
_dirfilter = dirfilter
|
|
|
else:
|
|
|
_dirfilter = None
|
|
|
|
|
|
_dirfullpath = lambda root, fn: path.join(root, fn)
|
|
|
|
|
|
if filetype:
|
|
|
def filefilter(fp):
|
|
|
link = path.islink(fp)
|
|
|
return ((FindFileType.SymbolicLink in filetype and link)
|
|
|
or (FindFileType.RegularFile in filetype and not link))
|
|
|
|
|
|
_filefilter = filefilter
|
|
|
else:
|
|
|
_filefilter = None
|
|
|
|
|
|
_filefullpath = lambda root, fn: path.join(root, fn)
|
|
|
|
|
|
if directory.endswith('/'):
|
|
|
clearlen = len(directory)
|
|
|
else:
|
|
|
clearlen = len(directory) + 1
|
|
|
shortpath = lambda fp: fp[clearlen:]
|
|
|
|
|
|
for root, directories, files in os.walk(directory):
|
|
|
for fn in files:
|
|
|
fp = _filefullpath(root, fn)
|
|
|
if not _filefilter or _filefilter(fp):
|
|
|
yield fp if fullpath else shortpath(fp)
|
|
|
for dn in directories[:]:
|
|
|
dp = _dirfullpath(root, dn)
|
|
|
if not _dirfilter or _dirfilter(dp):
|
|
|
yield dp if fullpath else shortpath(dp)
|
|
|
if _downfilter and not _downfilter(dp):
|
|
|
directories.remove(dn)
|
|
|
|
|
|
|
|
|
def isEmpty(dn):
|
|
|
"""
|
|
|
Проверить пустая ли директория
|
|
|
:param dn: директория
|
|
|
:return:
|
|
|
"""
|
|
|
return not listDirectory(dn)
|
|
|
|
|
|
|
|
|
def copyWithPath(fn, dest, prefix=None):
|
|
|
"""
|
|
|
Копировать файл в dst с созданием каталогов
|
|
|
|
|
|
dest: каталог для копирывания
|
|
|
fn: имя файла
|
|
|
prefix: префикс исходного файла
|
|
|
|
|
|
Пример:
|
|
|
copyWithPath("/etc/conf.d/net", "/tmp/conf", prefix="/etc")
|
|
|
/etc/conf.d/net -> /tmp/conf/conf.d/net
|
|
|
"""
|
|
|
if prefix:
|
|
|
destFile = pathJoin(dest, fn[len(prefix):])
|
|
|
else:
|
|
|
destFile = pathJoin(dest, path.basename(fn))
|
|
|
try:
|
|
|
destDir = path.dirname(destFile)
|
|
|
if not path.exists(destDir):
|
|
|
os.makedirs(destDir)
|
|
|
copy2(fn, destFile)
|
|
|
except Exception:
|
|
|
raise FilesError(
|
|
|
_("Failed to copy '%(src)s' to '%(dst)s'") %
|
|
|
{'src': fn,
|
|
|
'dst': destFile})
|
|
|
|
|
|
|
|
|
def getLoopFromPath(directory):
|
|
|
"""
|
|
|
Получить loop, использующие файлы в данной директории
|
|
|
"""
|
|
|
losetup = getProgPath('losetup')
|
|
|
p = process(losetup, '-a')
|
|
|
rePattern = re.compile('(^/dev/loop[^:]+)[^(]+\(([^)]+)\).*')
|
|
|
return map(lambda x: x[0],
|
|
|
filter(lambda x: x[1].startswith(directory),
|
|
|
reSearch(rePattern,
|
|
|
p.readlines())))
|
|
|
|
|
|
|
|
|
def getMdRaidDevices():
|
|
|
"""
|
|
|
Получить словарь: какой mdraid какие использует устройства
|
|
|
"""
|
|
|
reMdInfo = re.compile('^(\w+)\s+:\s+active.*?raid\d+\s+(.*)')
|
|
|
return dict(map(lambda x: (x[0], map(lambda x: x.partition('[')[0],
|
|
|
x[1].split())),
|
|
|
reSearch(reMdInfo, readLinesFile('/proc/mdstat'))))
|
|
|
|
|
|
|
|
|
def sambaPasswordCheck(username, password, server, resource):
|
|
|
"""
|
|
|
Подключиться к указанному samba ресурсу сервера по логину паролю
|
|
|
"""
|
|
|
smbclient = getProgPath('/usr/sbin/smbclient')
|
|
|
if not smbclient:
|
|
|
raise FilesError(_("Command not found '%s'") % "smbclint")
|
|
|
p = process(smbclient, "-U", username, "//%s/%s" % (server, resource),
|
|
|
envdict={'PASSWD': password})
|
|
|
return p.success()
|
|
|
|
|
|
|
|
|
class PercentProgress(processProgress):
|
|
|
"""
|
|
|
Объект выдает прогресс, ища в выводе \d+%
|
|
|
|
|
|
Args:
|
|
|
part: количество прогрессов в программе
|
|
|
delimeter: разделители строк по умолчанию \n и \r
|
|
|
cachefilter: фильтр вывода программы (регулярная строка)
|
|
|
startpart: используется для вывода процентов прогрессбар
|
|
|
с определенного места (0 по умолчанию)
|
|
|
end: конечный прогрессбар (по умолчанию)
|
|
|
atty: для получения данных создается pty
|
|
|
"""
|
|
|
|
|
|
def init(self, *args, **kwargs):
|
|
|
self.rePerc = re.compile("(\d+(?:\.\d+)?)%", re.S)
|
|
|
self.part = kwargs.get("part", 1)
|
|
|
if self.part < 1:
|
|
|
self.part = 1
|
|
|
self.add_offset = 100 / self.part
|
|
|
self.offset = 0 + kwargs.get("startpart", 0) * self.add_offset
|
|
|
self.is_end = kwargs.get("end", True)
|
|
|
self.stderr = STDOUT
|
|
|
self.delimeter = re.compile("[%s]" % kwargs.get("delimeter", "\n\r"))
|
|
|
self.cachedata = re.compile(kwargs.get("cachefilter",
|
|
|
"((?:\[31;01m\*|\[33;01m\*|"
|
|
|
"Bad Option|No space left|FATAL ERROR|SYNTAX:|mkisofs:|"
|
|
|
"error:|warning:|fatal:).*)"))
|
|
|
self.atty = kwargs.get("atty", False)
|
|
|
self.alldata = ""
|
|
|
|
|
|
def _open(self):
|
|
|
self.master, self.slave = None, None
|
|
|
if self.atty:
|
|
|
if not self.pipe:
|
|
|
self.master, self.slave = os.openpty()
|
|
|
import termios, struct, fcntl
|
|
|
|
|
|
TIOCSWINSZ = getattr(termios, 'TIOCSWINSZ', -2146929561)
|
|
|
s = struct.pack('HHHH', 25, 80, 0, 0)
|
|
|
fcntl.ioctl(self.slave, TIOCSWINSZ, s)
|
|
|
self.pipe = Popen(self.command,
|
|
|
stdout=self.slave,
|
|
|
stderr=self.slave,
|
|
|
cwd=self.cwd,
|
|
|
close_fds=True,
|
|
|
env=self.envdict)
|
|
|
self.pipe.stdout = os.fdopen(self.master, 'r')
|
|
|
else:
|
|
|
process._open(self)
|
|
|
|
|
|
def processInit(self):
|
|
|
self.percent = 0
|
|
|
self.showval = 0
|
|
|
if not self.offset:
|
|
|
return 0
|
|
|
|
|
|
def processEnd(self):
|
|
|
if not self.slave is None:
|
|
|
os.close(self.slave)
|
|
|
if self.is_end:
|
|
|
self.percent = 100
|
|
|
return 100
|
|
|
|
|
|
def processString(self, strdata):
|
|
|
self.alldata += strdata
|
|
|
match = self.rePerc.search(strdata)
|
|
|
resSearch = self.cachedata.search(strdata)
|
|
|
if resSearch:
|
|
|
self._cachedata.append(
|
|
|
re.sub("\[31;01m\*|\[33;01m\*|\[.*?m",
|
|
|
"", resSearch.group(1)))
|
|
|
if match:
|
|
|
percent = int(float(match.group(1)))
|
|
|
if percent < self.percent:
|
|
|
self.offset += self.add_offset
|
|
|
percent /= self.part
|
|
|
if percent != self.percent:
|
|
|
self.percent = percent
|
|
|
showval = min(99, self.percent + self.offset)
|
|
|
if showval != self.showval:
|
|
|
self.showval = showval
|
|
|
return self.showval
|
|
|
|
|
|
|
|
|
def set_active_tty(ttynum):
|
|
|
"""
|
|
|
Установить активный терминал
|
|
|
"""
|
|
|
chvt = getProgPath("/usr/bin/chvt")
|
|
|
if chvt:
|
|
|
os.system('chvt %d &>/dev/null' % ttynum)
|
|
|
|
|
|
|
|
|
def get_active_tty():
|
|
|
"""
|
|
|
Получить активный терминал
|
|
|
"""
|
|
|
getvt = getProgPath("/usr/bin/fgconsole")
|
|
|
if getvt:
|
|
|
p = process(getvt)
|
|
|
if p.success():
|
|
|
return p.read().strip()
|
|
|
return None
|
|
|
|
|
|
|
|
|
class scanDirectory(object):
|
|
|
"""Класс для cканирования директории"""
|
|
|
|
|
|
def processingFile(self, pathname, prefix):
|
|
|
"""Обработка в случае файла"""
|
|
|
return True
|
|
|
|
|
|
def processingDirectory(self, pathname, prefix):
|
|
|
"""Обработка в случае директории если возвращаем None то пропуск дир."""
|
|
|
return True
|
|
|
|
|
|
def scanningDirectory(self, scanDir, skipFile=(), skipDir=(),
|
|
|
prefix=None, flagDir=False):
|
|
|
"""Сканирование и обработка шаблонов в директории scanDir"""
|
|
|
ret = True
|
|
|
if not prefix:
|
|
|
prefix = path.join(scanDir, "")[:-1]
|
|
|
if not flagDir:
|
|
|
# проверка корневой директории
|
|
|
retDir = self.processingDirectory(scanDir, scanDir)
|
|
|
if retDir is None:
|
|
|
return None
|
|
|
elif retDir is False:
|
|
|
return False
|
|
|
if flagDir or stat.S_ISDIR(os.lstat(scanDir)[stat.ST_MODE]):
|
|
|
for absPath in sorted(listDirectory(scanDir, fullPath=True)):
|
|
|
relPath = absPath.split(prefix)[1]
|
|
|
stInfo = os.lstat(absPath)
|
|
|
statInfo = stInfo[stat.ST_MODE]
|
|
|
if stat.S_ISREG(statInfo):
|
|
|
# Обработка файла
|
|
|
if relPath in skipFile:
|
|
|
continue
|
|
|
if not self.processingFile(absPath, prefix):
|
|
|
return False
|
|
|
elif stat.S_ISDIR(statInfo):
|
|
|
# Обработка директории
|
|
|
if relPath in skipDir:
|
|
|
continue
|
|
|
retDir = self.processingDirectory(absPath, prefix)
|
|
|
if retDir is None:
|
|
|
continue
|
|
|
elif retDir is False:
|
|
|
return False
|
|
|
ret = self.scanningDirectory(absPath, skipFile,
|
|
|
skipDir, prefix, True)
|
|
|
if ret is False:
|
|
|
return False
|
|
|
return ret
|
|
|
|
|
|
|
|
|
class DiskSpaceError(Exception):
|
|
|
pass
|
|
|
|
|
|
|
|
|
class DiskSpace(object):
|
|
|
def __init__(self):
|
|
|
self.df_cmd = getProgPath('/bin/df')
|
|
|
|
|
|
def get_free(self, dev=None, dn=None):
|
|
|
if dev:
|
|
|
mp = isMount(dev)
|
|
|
if not mp:
|
|
|
raise DiskSpaceError(_("Device %s must be mounted") % dev)
|
|
|
dn = dev
|
|
|
p = process(self.df_cmd, dn, "-B1")
|
|
|
if p.success():
|
|
|
data = p.read().strip()
|
|
|
lines = data.split('\n')
|
|
|
if len(lines) >= 2:
|
|
|
cols = filter(None, lines[1].split())
|
|
|
if len(cols) == 6:
|
|
|
return int(cols[3])
|
|
|
raise DiskSpaceError(_("Wrong df output:\n%s") % data)
|
|
|
else:
|
|
|
raise DiskSpaceError(str(p.readerr()))
|