From 17bda7d30a791f843d5d6279f5a95e665b2387ca Mon Sep 17 00:00:00 2001 From: Joseph Hunkeler Date: Sun, 9 Jun 2013 19:48:41 -0400 Subject: Add AlignPermissions task --- ipsutils/tasks.py | 174 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 112 insertions(+), 62 deletions(-) diff --git a/ipsutils/tasks.py b/ipsutils/tasks.py index ae6ecb4..d376f38 100644 --- a/ipsutils/tasks.py +++ b/ipsutils/tasks.py @@ -14,8 +14,12 @@ # along with ipsutils. If not, see . from . import task from . import tpl +from collections import OrderedDict +import grp import os +import pwd import string +import shlex import shutil import subprocess import sys @@ -27,21 +31,21 @@ import zipfile class Lint(task.Task): def __init__(self, *args, **kwargs): """Execute LINT engine. - In order to facility continued use, a cache directory is created - in the user's home directory to store pkglint manifests. There is - *nothing* fast about pkglint (even with the cache). + In order to facility continued use, a cache directory is created + in the user's home directory to store pkglint manifests. There is + *nothing* fast about pkglint (even with the cache). """ super(Lint, self).__init__(self, *args, **kwargs) self.name = "Package LINT checking (this may take a while...)" self.cachedir = os.path.join(os.environ['HOME'], '.ipscache') - + def _check_cache(self): if not os.path.exists(self.cachedir): print("Creating cache directory: {0:s}".format(self.cachedir)) os.mkdir(self.cachedir) return False return True - + def _create_cache(self): command_pkg = [self.cls.tool['pkglint'], '-c', @@ -50,14 +54,14 @@ class Lint(task.Task): 'http://pkg.oracle.com/solaris/release/', self.cls.env_meta['STAGE4']] print("Creating cache (this may take a while...)") - proc_pkg = subprocess.Popen(command_pkg, stderr=subprocess.STDOUT) + proc_pkg = subprocess.Popen(command_pkg, stderr=subprocess.STDOUT) err = proc_pkg.wait() return err - + def task(self): if not self._check_cache(): self._create_cache() - + command_pkg = [self.cls.tool['pkglint'], '-c', self.cachedir, @@ -65,8 +69,8 @@ class Lint(task.Task): proc_pkg = subprocess.Popen(command_pkg, stderr=subprocess.STDOUT) err = proc_pkg.wait() return err - - + + class Resolve_Dependencies(task.Task): def __init__(self, *args, **kwargs): """Automatically resolve dependencies. @@ -74,7 +78,7 @@ class Resolve_Dependencies(task.Task): """ super(Resolve_Dependencies, self).__init__(self, *args, **kwargs) self.name = "Dependency resolution" - + def task(self): command_pkg = [self.cls.tool['pkgdepend'], 'resolve', @@ -99,19 +103,12 @@ class Dependencies(task.Task): '-md', self.cls.env_pkg['BUILDPROTO'], self.cls.env_meta['STAGE1_PASS2']] - command_pkgfmt = [self.cls.tool['pkgfmt']] fp = file(self.cls.env_meta['STAGE3'], 'w+') - proc_pkg = subprocess.Popen(command_pkg, - stdout=subprocess.PIPE) - proc_pkgfmt = subprocess.Popen(command_pkgfmt, - stdin=proc_pkg.stdout, - stdout=fp) - output, err = proc_pkgfmt.communicate() + proc_pkg = subprocess.Popen(command_pkg, stdout=fp) + err = proc_pkg.wait() fp.close() - if self.cls.options.verbose: - if output: - for line in output: - print(">>> {0:s}".format(line)) + if err <= 1: + err = 0 return err @@ -119,8 +116,8 @@ class Transmogrify(task.Task): def __init__(self, *args, **kwargs): """Using pkgmogrify's format in %transforms, a user may define specific file modifications on the package manifest - - For specification see: + + For specification see: man pkgmogrify(1) """ super(Transmogrify, self).__init__(self, *args, **kwargs) @@ -131,7 +128,6 @@ class Transmogrify(task.Task): '-DARCH={0:s}'.format(self.cls.key_dict['arch']), self.cls.env_meta['STAGE1'], self.cls.env_meta['STAGE2']] - command_pkgfmt = [self.cls.tool['pkgfmt']] fp = file(self.cls.env_meta['STAGE2'], 'w+') # Write %transforms block into transmogrification file # Proper syntax required. @@ -141,18 +137,9 @@ class Transmogrify(task.Task): fp.close() fp = file(self.cls.env_meta['STAGE1_PASS2'], 'w+') - proc_pkg = subprocess.Popen(command_pkg, - stdout=subprocess.PIPE) - proc_pkgfmt = subprocess.Popen(command_pkgfmt, - stdin=proc_pkg.stdout, - stdout=fp) - output, err = proc_pkgfmt.communicate() + proc_pkg = subprocess.Popen(command_pkg, stdout=fp) + err = proc_pkg.wait() fp.close() - - if self.cls.options.verbose: - if output: - for line in output: - print(">>> {0:s}".format(line)) return err @@ -162,24 +149,15 @@ class Manifest(task.Task): """ super(Manifest, self).__init__(self, *args, **kwargs) self.name = "Generate file manifest" - + def task(self): command_pkg = [self.cls.tool['pkgsend'], 'generate', self.cls.env_pkg['BUILDPROTO']] - command_pkgfmt = [self.cls.tool['pkgfmt']] fp = file(self.cls.env_meta['STAGE1'], 'a') - proc_pkg = subprocess.Popen(command_pkg, - stdout=subprocess.PIPE) - proc_pkgfmt = subprocess.Popen(command_pkgfmt, - stdin=proc_pkg.stdout, - stdout=fp) - output, err = proc_pkgfmt.communicate() + proc_pkg = subprocess.Popen(command_pkg, stdout=fp) + err = proc_pkg.wait() fp.close() - if self.cls.options.verbose: - if output: - for line in output: - print(">>> {0:s}".format(line)) return err @@ -198,14 +176,14 @@ class Unpack(task.Task): archive.extractall(dest) archive.close() return True - + def unzip(self, src, dest): if not zipfile.is_zipfile(src): return False archive = zipfile.ZipFile(src) archive.extractall(dest) return True - + def task(self): path = os.path.abspath(self.cls.env_pkg['SOURCES']) if not os.path.exists(path): @@ -232,10 +210,10 @@ class Unpack(task.Task): class Buildroot(task.Task): def __init__(self, *args, **kwargs): """Destroy/Create BUILDROOT per execution to keep the environment stable - """ + """ super(Buildroot, self).__init__(self, *args, **kwargs) self.name = "Create build root" - + def task(self): path = self.cls.env_pkg['BUILDROOT'] if os.path.exists(path): @@ -251,7 +229,7 @@ class Metadata(task.Task): """ super(Metadata, self).__init__(self, *args, **kwargs) self.name = "Generate meta data" - + def task(self): # A common problem that needs solving. What if you need to name your # package something else? "repackage" @@ -259,7 +237,7 @@ class Metadata(task.Task): repackas = 'repackage' else: repackas = 'name' - + output = [] meta_map = { 'pkg.fmri': self.cls.key_dict['group'] + "/" + @@ -286,7 +264,7 @@ class Metadata(task.Task): metadata = file(os.path.join(self.cls.env_meta['STAGE1']), 'w+') for line in output: if self.cls.options.verbose: - print(">>> {0:s}".format(line)) + print(">>> {0:s}".format(line)) metadata.writelines(line) metadata.close() return True @@ -301,7 +279,7 @@ class Script(task.Task): """ super(Script, self).__init__(self, *args, **kwargs) self.script = script - + def task(self): shebang = "#!/bin/bash\n" fp_tempfile = tempfile.NamedTemporaryFile('w+', prefix='ipsutils_', suffix='.sh', delete=True) @@ -324,7 +302,7 @@ class Script(task.Task): proc = subprocess.Popen(script, stdout=sys.stdout) err = proc.wait() fp_tempfile.close() - + os.chdir(self.cls.env['IPSBUILD']) return err @@ -337,7 +315,7 @@ class Package(task.Task): is a need. """ super(Package, self).__init__(self, *args, **kwargs) - self.name = "Generate package" + self.name = "Generate package" self.spkg = False self.source = self.cls.env_pkg['BUILDROOT'] self.destination = self.cls.env_pkg['PKGS'] @@ -345,11 +323,11 @@ class Package(task.Task): self.spkg = True self.name = "Generate source package" self.destination = self.cls.env_pkg['SPKGS'] - + def task(self): if os.path.exists(self.destination): shutil.rmtree(self.destination) - + if self.spkg: os.mkdir(self.destination) shutil.copy2(self.cls.ipsfile, self.destination) @@ -359,6 +337,78 @@ class Package(task.Task): os.path.join(self.destination, \ os.path.basename(self.cls.env_pkg['BUILDPROTO']))) shutil.copy2(self.cls.env_meta['STAGE4'], self.destination) - - return 0 + return 0 + +class AlignPermissions(task.Internal): + def __init__(self, *args, **kwargs): + """Reduces the need for manual transmogrification. + IPS ability to generate a manifest that is compatible with itself is + incredibly bad. Permissions in a manifest never match the system's + directory permissions. + + In order to fix the situation we reference the manifest permissions and + the system permissions, then replace the incorrect lines with the + proper values. + """ + super(AlignPermissions, self).__init__(self, *args, **kwargs) + self.name = "Automatic permission alignment" + self.filename = self.cls.env_meta['STAGE4'] + + def task(self): + system_paths = [] + manifest_paths = [] + corrections = [] + + line_number = 1 + for line in file(self.filename): + if line.startswith('dir'): + line = line.lstrip('dir') + tokens = shlex.split(line) + path = OrderedDict() + for token in tokens: + s = token.split('=') + path[s[0]] = s[1] + path['line'] = line_number + manifest_paths.append(path) + line_number += 1 + + # Check manifest real paths against the real system paths + # Rewrite manifest with the proper system-level permissions to prevent the + # IPS from bombing upon installing simple packages with slightly different + # permissions. + for path in manifest_paths: + for k, v in path.items(): + if 'path' in k: + real = os.path.join('/', v) + try: + si = os.stat(os.path.realpath(real)) + owner = pwd.getpwuid(si.st_uid).pw_name + group = grp.getgrgid(si.st_gid).gr_name + mode = si.st_mode & 0777 + replacement = 'dir {}={} owner={} group={} mode={}'.format(k, v, owner, group, oct(mode)) + self.corrections.append({path['line']: replacement}) + except: + continue + line_number = 1 + found = False + infile = file(self.filename, 'r') + outfile = tempfile.NamedTemporaryFile(delete=True) + + for line in infile.readlines(): + for correction in corrections: + for pos, key in correction.items(): + if line_number == pos: + found = True + if self.cls.options.verbose: + print("Correcting line WANT:{}, GOT:{}".format(line_number, pos)) + outfile.write(key + '\n') + if not found: + outfile.writelines(line) + else: + found = False + line_number += 1 + + infile.close() + shutil.copyfile(outfile.name, self.filename) + outfile.close() -- cgit