#!/usr/bin/python
|
#
|
# CDDL HEADER START
|
#
|
# The contents of this file are subject to the terms of the
|
# Common Development and Distribution License (the "License").
|
# You may not use this file except in compliance with the License.
|
#
|
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
# or http://www.opensolaris.org/os/licensing.
|
# See the License for the specific language governing permissions
|
# and limitations under the License.
|
#
|
# When distributing Covered Code, include this CDDL HEADER in each
|
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
# If applicable, add the following below this CDDL HEADER, with the
|
# fields enclosed by brackets "[]" replaced with your own identifying
|
# information: Portions Copyright [yyyy] [name of copyright owner]
|
#
|
# CDDL HEADER END
|
#
|
|
#
|
# Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
|
#
|
|
# Some userland consolidation specific lint checks
|
|
import pkg.lint.base as base
|
import pkg.elf as elf
|
import re
|
import os.path
|
|
|
class UserlandActionChecker(base.ActionChecker):
|
"""An opensolaris.org-specific class to check actions."""
|
|
name = "userland.action"
|
|
def __init__(self, config):
|
self.description = _(
|
"checks Userland packages for common content errors")
|
path = os.getenv('PROTO_PATH')
|
if path != None:
|
self.proto_path = path.split()
|
else:
|
self.proto_path = None
|
self.runpath_re = [
|
re.compile('^/lib(/.*)?$'),
|
re.compile('^/usr/'),
|
re.compile('^\$ORIGIN/')
|
]
|
self.initscript_re = re.compile("^etc/(rc.|init)\.d")
|
super(UserlandActionChecker, self).__init__(config)
|
|
def startup(self, engine):
|
pass
|
|
def __realpath(self, path, target):
|
"""Combine path and target to get the real path."""
|
|
result = os.path.dirname(path)
|
|
for frag in target.split(os.sep):
|
if frag == '..':
|
result = os.path.dirname(result)
|
elif frag == '.':
|
pass
|
else:
|
result = os.path.join(result, frag)
|
|
return result
|
|
def __elf_runpath_check(self, path):
|
result = None
|
list = []
|
|
ed = elf.get_dynamic(path)
|
for dir in ed.get("runpath", "").split(":"):
|
if dir == None or dir == '':
|
continue
|
|
match = False
|
for expr in self.runpath_re:
|
if expr.match(dir):
|
match = True
|
break
|
|
if match == False:
|
list.append(dir)
|
|
if len(list) > 0:
|
result = _("bad RUNPATH, '%%s' includes '%s'" %
|
":".join(list))
|
|
return result
|
|
def __elf_wrong_location_check(self, path):
|
result = None
|
|
ei = elf.get_info(path)
|
bits = ei.get("bits")
|
elems = os.path.dirname(path).split("/")
|
|
if ("amd64" in elems) or ("sparcv9" in elems) or ("64" in elems):
|
path64 = True
|
else:
|
path64 = False
|
|
if bits == 32 and path64:
|
result = _("32-bit object '%s' in 64-bit path")
|
elif bits == 64 and not path64:
|
result = _("64-bit object '%s' in 32-bit path")
|
return result
|
|
def file_action(self, action, manifest, engine, pkglint_id="001"):
|
"""Checks for existence in the proto area."""
|
|
if action.name not in ["file"]:
|
return
|
|
path = action.hash
|
if path == None or path == 'NOHASH':
|
path = action.attrs["path"]
|
|
# check for writable files without a preserve attribute
|
if "mode" in action.attrs:
|
mode = action.attrs["mode"]
|
|
if (int(mode, 8) & 0222) != 0 and "preserve" not in action.attrs:
|
engine.error(
|
_("%(path)s is writable (%(mode)s), but missing a preserve"
|
" attribute") % {"path": path, "mode": mode},
|
msgid="%s%s.0" % (self.name, pkglint_id))
|
elif "preserve" in action.attrs:
|
if "mode" in action.attrs:
|
mode = action.attrs["mode"]
|
if (int(mode, 8) & 0222) == 0:
|
engine.error(
|
_("%(path)s has a preserve action, but is not writable (%(mode)s)") % {"path": path, "mode": mode},
|
msgid="%s%s.4" % (self.name, pkglint_id))
|
else:
|
engine.error(
|
_("%(path)s has a preserve action, but no mode") % {"path": path, "mode": mode},
|
msgid="%s%s.3" % (self.name, pkglint_id))
|
|
# checks that require a physical file to look at
|
if self.proto_path is not None:
|
for directory in self.proto_path:
|
fullpath = directory + "/" + path
|
|
if os.path.exists(fullpath):
|
break
|
|
if not os.path.exists(fullpath):
|
engine.info(
|
_("%s missing from proto area, skipping"
|
" content checks") % path,
|
msgid="%s%s.1" % (self.name, pkglint_id))
|
elif elf.is_elf_object(fullpath):
|
# 32/64 bit in wrong place
|
result = self.__elf_wrong_location_check(fullpath)
|
if result != None:
|
engine.error(result % path,
|
msgid="%s%s.2" % (self.name, pkglint_id))
|
result = self.__elf_runpath_check(fullpath)
|
if result != None:
|
engine.error(result % path,
|
msgid="%s%s.3" % (self.name, pkglint_id))
|
|
file_action.pkglint_desc = _("Paths should exist in the proto area.")
|
|
def link_resolves(self, action, manifest, engine, pkglint_id="002"):
|
"""Checks for link resolution."""
|
|
if action.name not in ["link", "hardlink"]:
|
return
|
|
path = action.attrs["path"]
|
target = action.attrs["target"]
|
realtarget = self.__realpath(path, target)
|
|
resolved = False
|
for maction in manifest.gen_actions():
|
mpath = None
|
if maction.name in ["dir", "file", "link",
|
"hardlink"]:
|
mpath = maction.attrs["path"]
|
|
if mpath and mpath == realtarget:
|
resolved = True
|
break
|
|
if resolved != True:
|
engine.error(
|
_("%s %s has unresolvable target '%s'") %
|
(action.name, path, target),
|
msgid="%s%s.0" % (self.name, pkglint_id))
|
|
link_resolves.pkglint_desc = _("links should resolve.")
|
|
def init_script(self, action, manifest, engine, pkglint_id="003"):
|
"""Checks for SVR4 startup scripts."""
|
|
if action.name not in ["file", "dir", "link", "hardlink"]:
|
return
|
|
path = action.attrs["path"]
|
if self.initscript_re.match(path):
|
engine.warning(
|
_("SVR4 startup '%s', deliver SMF"
|
" service instead") % path,
|
msgid="%s%s.0" % (self.name, pkglint_id))
|
|
init_script.pkglint_desc = _(
|
"SVR4 startup scripts should not be delivered.")
|
|
class UserlandManifestChecker(base.ManifestChecker):
|
"""An opensolaris.org-specific class to check manifests."""
|
|
name = "userland.manifest"
|
|
def __init__(self, config):
|
super(UserlandManifestChecker, self).__init__(config)
|
|
def license_check(self, manifest, engine, pkglint_id="001"):
|
manifest_paths = []
|
files = False
|
license = False
|
|
for action in manifest.gen_actions_by_type("file"):
|
files = True
|
break
|
|
if files == False:
|
return
|
|
for action in manifest.gen_actions_by_type("license"):
|
return
|
|
engine.error( _("missing license action"),
|
msgid="%s%s.0" % (self.name, pkglint_id))
|
|
license_check.pkglint_dest = _(
|
"license actions are required if you deliver files.")
|