2009-12-15 14:26:01 +00:00
|
|
|
#
|
2010-02-23 13:20:05 +00:00
|
|
|
# sysutils.py
|
2010-01-12 11:45:54 +00:00
|
|
|
#
|
|
|
|
# Copyright (C) 2009 Red Hat, Inc.
|
|
|
|
#
|
|
|
|
# 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, see <http://www.gnu.org/licenses/>.
|
|
|
|
#
|
|
|
|
# Red Hat Author(s): Martin Gracik <mgracik@redhat.com>
|
2009-12-15 14:26:01 +00:00
|
|
|
#
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
__all__ = ["mkdir_", "makedirs_", "remove_", "symlink_", "touch_",
|
|
|
|
"chown_", "chmod_", "replace_", "scopy_", "dcopy_"]
|
|
|
|
|
|
|
|
|
2009-12-15 14:26:01 +00:00
|
|
|
import sys
|
|
|
|
import os
|
|
|
|
import shutil
|
|
|
|
import glob
|
|
|
|
import fileinput
|
|
|
|
import re
|
|
|
|
import pwd
|
|
|
|
import grp
|
|
|
|
import commands
|
|
|
|
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
class SysUtilsError(Exception):
|
|
|
|
pass
|
2009-12-15 14:26:01 +00:00
|
|
|
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
class SmartCopyError(SysUtilsError):
|
|
|
|
pass
|
2009-12-15 14:26:01 +00:00
|
|
|
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
class LinkerError(SysUtilsError):
|
|
|
|
pass
|
2009-12-15 14:26:01 +00:00
|
|
|
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
def mkdir_(dir):
|
|
|
|
if not os.path.isdir(dir):
|
|
|
|
os.mkdir(dir)
|
2009-12-15 14:26:01 +00:00
|
|
|
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
def makedirs_(dir):
|
|
|
|
if not os.path.isdir(dir):
|
|
|
|
os.makedirs(dir)
|
2009-12-15 14:26:01 +00:00
|
|
|
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
def remove_(path):
|
|
|
|
for fname in glob.iglob(path):
|
|
|
|
if os.path.islink(fname) or os.path.isfile(fname):
|
|
|
|
os.unlink(fname)
|
|
|
|
else:
|
|
|
|
shutil.rmtree(fname)
|
2009-12-15 14:26:01 +00:00
|
|
|
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
def symlink_(link_target, link_name, force=True):
|
|
|
|
if force and (os.path.islink(link_name) or os.path.isfile(link_name)):
|
2009-12-15 14:26:01 +00:00
|
|
|
os.unlink(link_name)
|
|
|
|
|
|
|
|
os.symlink(link_target, link_name)
|
|
|
|
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
def touch_(fname):
|
|
|
|
if os.path.exists(fname):
|
|
|
|
os.utime(fname, None)
|
2009-12-15 14:26:01 +00:00
|
|
|
else:
|
2010-02-23 13:20:05 +00:00
|
|
|
with open(fname, "w") as f:
|
2009-12-15 14:26:01 +00:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
def chown_(path, user=None, group=None, recursive=False):
|
2009-12-15 14:26:01 +00:00
|
|
|
uid = gid = -1
|
|
|
|
|
|
|
|
if user is not None:
|
|
|
|
uid = pwd.getpwnam(user)[2]
|
|
|
|
if group is not None:
|
|
|
|
gid = grp.getgrnam(group)[2]
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
for fname in glob.iglob(path):
|
2009-12-15 14:26:01 +00:00
|
|
|
os.chown(fname, uid, gid)
|
|
|
|
|
|
|
|
if recursive and os.path.isdir(fname):
|
|
|
|
for nested in os.listdir(fname):
|
|
|
|
nested = os.path.join(fname, nested)
|
2010-02-23 13:20:05 +00:00
|
|
|
chown_(nested, user, group, recursive)
|
2009-12-15 14:26:01 +00:00
|
|
|
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
def chmod_(path, mode, recursive=False):
|
|
|
|
for fname in glob.iglob(path):
|
2009-12-15 14:26:01 +00:00
|
|
|
os.chmod(fname, mode)
|
|
|
|
|
|
|
|
if recursive and os.path.isdir(fname):
|
|
|
|
for nested in os.listdir(fname):
|
|
|
|
nested = os.path.join(fname, nested)
|
2010-02-23 13:20:05 +00:00
|
|
|
chmod_(nested, mode, recursive)
|
2009-12-15 14:26:01 +00:00
|
|
|
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
def replace_(fname, find, replace):
|
|
|
|
fin = fileinput.input(fname, inplace=1)
|
|
|
|
pattern = re.compile(find)
|
2009-12-15 14:26:01 +00:00
|
|
|
|
|
|
|
for line in fin:
|
2010-02-23 13:20:05 +00:00
|
|
|
line = pattern.sub(replace, line)
|
2009-12-15 14:26:01 +00:00
|
|
|
sys.stdout.write(line)
|
|
|
|
|
|
|
|
fin.close()
|
|
|
|
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
def scopy_(src_path, dst_path, src_root="/", dst_root="/", symlinks=True,
|
|
|
|
ignore_errors=False):
|
|
|
|
|
|
|
|
__copy(src_path, dst_path, src_root, dst_root,
|
|
|
|
symlinks, deps=False, ignore_errors=ignore_errors)
|
|
|
|
|
2009-12-15 14:26:01 +00:00
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
def dcopy_(src_path, dst_path, src_root="/", dst_root="/", symlinks=True,
|
|
|
|
ignore_errors=False):
|
2009-12-15 14:26:01 +00:00
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
__copy(src_path, dst_path, src_root, dst_root,
|
|
|
|
symlinks, deps=True, ignore_errors=ignore_errors)
|
|
|
|
|
|
|
|
|
|
|
|
def __copy(src_path, dst_path, src_root="/", dst_root="/",
|
|
|
|
symlinks=True, deps=False, ignore_errors=False):
|
|
|
|
|
|
|
|
if not src_root.endswith("/"):
|
|
|
|
src_root += "/"
|
|
|
|
if not dst_root.endswith("/"):
|
|
|
|
dst_root += "/"
|
|
|
|
|
|
|
|
smartcopy = SmartCopy(src_root, dst_root, symlinks, deps, ignore_errors)
|
|
|
|
|
|
|
|
src = os.path.join(src_root, src_path)
|
|
|
|
pattern = re.compile(r"(\*|\?|\[.*?\])")
|
|
|
|
if pattern.search(src):
|
|
|
|
fnames = glob.glob(src)
|
|
|
|
else:
|
|
|
|
fnames = [src]
|
|
|
|
|
|
|
|
if not fnames and not ignore_errors:
|
|
|
|
err_msg = "cannot stat '{0}': No such file or directory"
|
|
|
|
raise SysUtilsError(err_msg.format(src))
|
|
|
|
|
|
|
|
for fname in fnames:
|
|
|
|
fname = fname.replace(src_root, "", 1)
|
|
|
|
smartcopy.copy(fname, dst_path)
|
|
|
|
|
|
|
|
smartcopy.process()
|
|
|
|
|
|
|
|
|
|
|
|
# XXX
|
2009-12-15 14:26:01 +00:00
|
|
|
class SmartCopy(object):
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
def __init__(self, src_root="/", dst_root="/", symlinks=True, deps=False,
|
2009-12-15 14:26:01 +00:00
|
|
|
ignore_errors=False):
|
|
|
|
|
|
|
|
self.src_root = src_root
|
|
|
|
self.dst_root = dst_root
|
|
|
|
self.symlinks = symlinks
|
2010-02-23 13:20:05 +00:00
|
|
|
self.deps = deps
|
2009-12-15 14:26:01 +00:00
|
|
|
self.ignore_errors = ignore_errors
|
|
|
|
|
|
|
|
self.linker = Linker(src_root)
|
|
|
|
|
|
|
|
self.clear()
|
|
|
|
|
|
|
|
def clear(self):
|
|
|
|
self.makedirs = []
|
|
|
|
self.copyfiles = set()
|
|
|
|
self.links = set()
|
|
|
|
self.errors = []
|
|
|
|
|
|
|
|
def copy(self, src_path, dst_path):
|
|
|
|
src = os.path.normpath(os.path.join(self.src_root, src_path))
|
|
|
|
dst = os.path.normpath(os.path.join(self.dst_root, dst_path))
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
# check if the source path exists
|
2009-12-15 14:26:01 +00:00
|
|
|
if not os.path.exists(src):
|
2010-02-23 13:20:05 +00:00
|
|
|
err_msg = "cannot stat '{0}': No such file or directory"
|
|
|
|
err_msg = err_msg.format(src)
|
2009-12-15 14:26:01 +00:00
|
|
|
if not self.ignore_errors:
|
|
|
|
raise SmartCopyError(err_msg)
|
|
|
|
else:
|
|
|
|
self.errors.append(err_msg)
|
2010-02-23 13:20:05 +00:00
|
|
|
return
|
2009-12-15 14:26:01 +00:00
|
|
|
|
|
|
|
if os.path.isfile(src):
|
|
|
|
self.__copy_file(src_path, dst_path, src, dst)
|
|
|
|
elif os.path.isdir(src):
|
|
|
|
self.__copy_dir(src_path, dst_path, src, dst)
|
|
|
|
|
|
|
|
def __copy_file(self, src_path, dst_path, src, dst):
|
|
|
|
# if destination is an existing directory,
|
2010-02-23 13:20:05 +00:00
|
|
|
# append the source filename to the destination
|
2009-12-15 14:26:01 +00:00
|
|
|
if os.path.isdir(dst):
|
|
|
|
dst = os.path.join(dst, os.path.basename(src))
|
|
|
|
|
|
|
|
# check if the new destination is still an existing directory
|
|
|
|
if os.path.isdir(dst):
|
2010-02-23 13:20:05 +00:00
|
|
|
err_msg = "cannot overwrite directory '{0}' with non-directory"
|
|
|
|
err_msg = err_msg.format(dst)
|
2009-12-15 14:26:01 +00:00
|
|
|
if not self.ignore_errors:
|
|
|
|
raise SmartCopyError(err_msg)
|
|
|
|
else:
|
|
|
|
self.errors.append(err_msg)
|
2010-02-23 13:20:05 +00:00
|
|
|
return
|
2009-12-15 14:26:01 +00:00
|
|
|
|
|
|
|
if os.path.islink(src):
|
2010-02-23 13:20:05 +00:00
|
|
|
self.__copy_link(src_path, dst_path, src, dst)
|
2009-12-15 14:26:01 +00:00
|
|
|
else:
|
|
|
|
self.copyfiles.add((src, dst))
|
|
|
|
|
|
|
|
def __copy_dir(self, src_path, dst_path, src, dst):
|
|
|
|
# append the source directory name to the destination path
|
|
|
|
dirname = os.path.basename(src)
|
|
|
|
new_dst = os.path.join(dst, dirname)
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
# remove the trailing "/"
|
2009-12-15 14:26:01 +00:00
|
|
|
if new_dst.endswith("/"):
|
|
|
|
new_dst = new_dst[:-1]
|
|
|
|
|
|
|
|
if os.path.islink(src):
|
2010-02-23 13:20:05 +00:00
|
|
|
self.__copy_link(src_path, dst_path, src, new_dst, dir=True)
|
2009-12-15 14:26:01 +00:00
|
|
|
else:
|
2010-02-23 13:20:05 +00:00
|
|
|
# create the destination directory
|
|
|
|
if not os.path.isdir(new_dst) and new_dst not in self.makedirs:
|
2009-12-15 14:26:01 +00:00
|
|
|
self.makedirs.append(new_dst)
|
|
|
|
elif os.path.isfile(new_dst):
|
2010-02-23 13:20:05 +00:00
|
|
|
err_msg = "cannot overwrite file '{0}' with directory"
|
|
|
|
err_msg = err_msg.format(new_dst)
|
2009-12-15 14:26:01 +00:00
|
|
|
if not self.ignore_errors:
|
|
|
|
raise SmartCopyError(err_msg)
|
|
|
|
else:
|
|
|
|
self.errors.append(err_msg)
|
2010-02-23 13:20:05 +00:00
|
|
|
return
|
2009-12-15 14:26:01 +00:00
|
|
|
|
|
|
|
new_dst_path = os.path.join(dst_path, dirname)
|
|
|
|
|
|
|
|
try:
|
|
|
|
fnames = os.listdir(src)
|
|
|
|
except OSError as why:
|
2010-02-23 13:20:05 +00:00
|
|
|
err_msg = "cannot list directory '{0}': {1}'"
|
|
|
|
err_msg = err_msg.format(src, why)
|
2009-12-15 14:26:01 +00:00
|
|
|
if not self.ignore_errors:
|
|
|
|
raise SmartCopyError(err_msg)
|
|
|
|
else:
|
|
|
|
self.errors.append(err_msg)
|
2010-02-23 13:20:05 +00:00
|
|
|
return
|
2009-12-15 14:26:01 +00:00
|
|
|
|
|
|
|
for fname in fnames:
|
|
|
|
fname = os.path.join(src_path, fname)
|
|
|
|
self.copy(fname, new_dst_path)
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
def __copy_link(self, src_path, dst_path, src, dst, dir=False):
|
|
|
|
if not self.symlinks:
|
|
|
|
# TODO
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2009-12-15 14:26:01 +00:00
|
|
|
# read the link target
|
|
|
|
link_target = os.readlink(src)
|
|
|
|
|
|
|
|
# get the target source and destination paths
|
|
|
|
target_src_path = os.path.join(os.path.dirname(src_path), link_target)
|
|
|
|
target_dst_dir = os.path.join(dst_path, os.path.dirname(link_target))
|
|
|
|
|
|
|
|
# if the link target is an absolute path,
|
|
|
|
# make sure we copy it relative to the dst_root
|
|
|
|
if target_dst_dir.startswith("/"):
|
|
|
|
target_dst_dir = target_dst_dir[1:]
|
|
|
|
|
|
|
|
# remove the trailing "/"
|
|
|
|
if target_dst_dir.endswith("/"):
|
|
|
|
target_dst_dir = target_dst_dir[:-1]
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
# create the destination directory
|
2009-12-15 14:26:01 +00:00
|
|
|
target_dst = os.path.join(self.dst_root, target_dst_dir)
|
2010-02-23 13:20:05 +00:00
|
|
|
if not os.path.isdir(target_dst) and target_dst not in self.makedirs:
|
2009-12-15 14:26:01 +00:00
|
|
|
self.makedirs.append(target_dst)
|
|
|
|
|
|
|
|
# copy the target along with the link
|
|
|
|
self.copy(target_src_path, target_dst_dir)
|
|
|
|
|
|
|
|
# create the symlink named dst, pointing to link_target
|
|
|
|
self.links.add((link_target, dst))
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
def __get_deps(self):
|
2009-12-15 14:26:01 +00:00
|
|
|
deps = set()
|
|
|
|
|
|
|
|
for src, dst in self.copyfiles:
|
|
|
|
if self.linker.is_elf(src):
|
|
|
|
deps = deps.union(self.linker.get_deps(src))
|
|
|
|
|
|
|
|
for src in deps:
|
|
|
|
src_path = src.replace(self.src_root, "", 1)
|
|
|
|
dst_path = os.path.dirname(src_path)
|
|
|
|
|
|
|
|
# create the destination directory
|
|
|
|
dst_dir = os.path.join(self.dst_root, dst_path)
|
2010-02-23 13:20:05 +00:00
|
|
|
if not os.path.isdir(dst_dir) and dst_dir not in self.makedirs:
|
2009-12-15 14:26:01 +00:00
|
|
|
self.makedirs.append(dst_dir)
|
|
|
|
|
|
|
|
self.copy(src_path, dst_path)
|
|
|
|
|
|
|
|
def process(self):
|
2010-02-23 13:20:05 +00:00
|
|
|
if self.deps:
|
|
|
|
self.__get_deps()
|
2009-12-15 14:26:01 +00:00
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
# create required directories
|
|
|
|
map(makedirs_, self.makedirs)
|
2009-12-15 14:26:01 +00:00
|
|
|
|
|
|
|
# remove the dst, if it is a link to src
|
|
|
|
for src, dst in self.copyfiles:
|
|
|
|
if os.path.realpath(dst) == src:
|
|
|
|
os.unlink(dst)
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
# copy all the files
|
2009-12-15 14:26:01 +00:00
|
|
|
map(lambda (src, dst): shutil.copy2(src, dst), self.copyfiles)
|
|
|
|
|
|
|
|
# create symlinks
|
2010-02-23 13:20:05 +00:00
|
|
|
map(lambda (target, name): symlink_(target, name), self.links)
|
2009-12-15 14:26:01 +00:00
|
|
|
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
# XXX
|
2009-12-15 14:26:01 +00:00
|
|
|
class Linker(object):
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
LIBDIRS = ("lib64",
|
|
|
|
"usr/lib64",
|
|
|
|
"lib",
|
|
|
|
"usr/lib")
|
|
|
|
|
|
|
|
LDDBIN = "/usr/bin/ldd"
|
|
|
|
FILEBIN = "/usr/bin/file"
|
2009-12-15 14:26:01 +00:00
|
|
|
|
|
|
|
def __init__(self, root="/"):
|
|
|
|
libdirs = map(lambda path: os.path.join(root, path), self.LIBDIRS)
|
|
|
|
libdirs = ":".join(libdirs)
|
|
|
|
|
|
|
|
ld_linux = None
|
2010-02-25 17:09:56 +00:00
|
|
|
pattern = re.compile(r'^RTLDLIST="?(?P<ld_linux>.*?)"?$')
|
2009-12-15 14:26:01 +00:00
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
with open(self.LDDBIN, "r") as f:
|
2009-12-15 14:26:01 +00:00
|
|
|
for line in f:
|
2010-02-23 13:20:05 +00:00
|
|
|
m = pattern.match(line.strip())
|
2009-12-15 14:26:01 +00:00
|
|
|
if m:
|
|
|
|
ld_linux = m.group("ld_linux")
|
|
|
|
break
|
|
|
|
|
2010-02-25 16:59:49 +00:00
|
|
|
if ld_linux:
|
|
|
|
ld_linux = filter(os.path.isfile, ld_linux.split())
|
|
|
|
|
|
|
|
if not ld_linux:
|
2010-02-23 13:20:05 +00:00
|
|
|
raise LinkerError("cannot find the ld_linux executable")
|
|
|
|
|
|
|
|
self.lddcmd = "LD_LIBRARY_PATH={0} {1} --list"
|
2010-02-25 16:59:49 +00:00
|
|
|
self.lddcmd = self.lddcmd.format(libdirs, ld_linux[0])
|
2009-12-15 14:26:01 +00:00
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
self.pattern = re.compile(r"^[-._/a-zA-Z0-9]+\s=>\s"
|
|
|
|
r"(?P<lib>[-._/a-zA-Z0-9]+)"
|
|
|
|
r"\s\(0x[0-9a-f]+\)$")
|
2009-12-15 14:26:01 +00:00
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
def is_elf(self, fname):
|
|
|
|
cmd = "{0} --brief {1}".format(self.FILEBIN, fname)
|
|
|
|
err, stdout = commands.getstatusoutput(cmd)
|
2009-12-15 14:26:01 +00:00
|
|
|
if err:
|
2010-02-23 13:20:05 +00:00
|
|
|
raise LinkerError(stdout)
|
2009-12-15 14:26:01 +00:00
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
if not stdout.count("ELF"):
|
2009-12-15 14:26:01 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
2010-02-23 13:20:05 +00:00
|
|
|
def get_deps(self, fname):
|
|
|
|
cmd = "{0} {1}".format(self.lddcmd, fname)
|
|
|
|
err, stdout = commands.getstatusoutput(cmd)
|
2009-12-15 14:26:01 +00:00
|
|
|
if err:
|
2010-02-23 13:20:05 +00:00
|
|
|
raise LinkerError(stdout)
|
2009-12-15 14:26:01 +00:00
|
|
|
|
|
|
|
deps = set()
|
2010-02-23 13:20:05 +00:00
|
|
|
for line in stdout.splitlines():
|
2009-12-15 14:26:01 +00:00
|
|
|
m = self.pattern.match(line.strip())
|
|
|
|
if m:
|
|
|
|
deps.add(m.group("lib"))
|
|
|
|
|
|
|
|
return deps
|